I found that there is a different execution order between two similar statements(the only difference is the below one has an additional ;). The destructor order is different. Does C++ have a corresponding specification about that or it's only an unspecified behavior?
Environment: GCC10
#include <iostream>
template <int num>
struct S {
S() {std::cout << "S(" << num << ")\n"; }
~S() {std::cout << "~S(" << num << ")\n"; }
};
int main() {
({S<1>(), S<2>();});
std::cout << "-----------------------------------\n";
({S<3>(), S<4>();;});
}
output:
S(1)
S(2)
~S(1)
~S(2)
-----------------------------------
S(3)
S(4)
~S(4)
~S(3)
This is not standard C++. This is a GCC extension known as statement expression. A compound statement enclosed in parentheses can appear where an expression is allowed. If the last statement in the brace-enclosed block is an expression statement, then the value of this expression is also the value of the overall statement expression; otherwise, the statement expression is of type void and has no value.
Your first example is roughly equivalent to
([](){ return S<1>(), S<2>(); })();
(that's a lambda that's created and then called immediately). There's a comma expression that creates S<1> and S<2> temporaries. S<1> is destroyed, S<2> is, technically, copied to the return value - but that copy is elided. If it weren't for this copy elision, you'd see
S<1>()
S<2>()
S<2>(S<2>&&) // (1) move constructor
~S<2>() // (2) the original temporary is destroyed
~S<1>()
~S<2>() // the copy is destroyed outside of the lambda
But the pair (1)/(2) is elided, leaving the sequence you observe in your example.
In the second example, the last statement within the braces is not an expression, so the whole thing doesn't have a value either. It's roughly equivalent to
([](){ S<3>(), S<4>(); return; })();
Both temporaries are created and destroyed within the lambda, and the usual rules apply - temporaries are destroyed in the reverse order of construction.
Related
#include <iostream>
#include <coroutine>
class eager {
public:
struct promise_type {
promise_type() { std::cout << "promise_type ctor" << std::endl; }
~promise_type() { std::cout << "~promise_type dtor" << std::endl; }
struct return_object {
return_object() { std::cout << "return_object ctor" << std::endl; }
~return_object() { std::cout << "~return_object dtor" << std::endl; }
operator eager() { return {}; }
};
auto get_return_object() noexcept { return return_object{}; }
constexpr auto initial_suspend() const noexcept { return std::suspend_never{}; }
constexpr auto final_suspend() const noexcept { return std::suspend_never{}; }
constexpr auto return_void() const noexcept {}
auto unhandled_exception() -> void { throw; }
};
};
auto coroutine() -> eager {
co_return;
}
auto main() -> int
{
coroutine();
return 0;
}
You can see the result for MSVC, clang and GCC here: https://godbolt.org/z/Yan9s9TPE
According to lots of articles about coroutine, the coroutine() will be transformed into...
auto coroutine() -> eager {
eager::promise_type promise;
auto res = promise.get_return_object();
// initial suspend
promise.return_void();
// final suspend
return res;
}
At a glance, since the promise object is constructed first, I thought it would be the last object to be destructed.
However, MSVC and GCC show reverse order:
// from MSVC/GCC
promise_type ctor
return_object ctor
~promise_type dtor
~return_object dtor
On the other hand, clang shows what I expected:
// from clang
promise_type ctor
return_object ctor
~return_object dtor
~promise_type dtor
Which one is right?
Or, is the destruction order of promise object and return object just unspecified by standard?
According to lots of articles about coroutine
Then "lots of articles about coroutine[sic]" are incorrect.
The result object is not on the coroutine stack. It cannot be on the coroutine stack, because it's the result object for the initial call to the coroutine.
All that the C++ standard says about get_result_object is this:
The expression promise.get_return_object() is used to initialize the glvalue result or prvalue result object of a call to a coroutine.
The call to get_return_object is sequenced before the call to initial_suspend and is invoked at most once.
It happens before initial_suspend and it gets called once. That's all it has to say. Therefore, everything else about result objects from functions work as normal; in this case, it simply gets initialized before the function properly starts instead of when the function is about to return.
By C++'s normal rules, a function's result object is on the caller's stack, not the stack of the function being called. So when promise.get_result_object() is evaluated, it is initializing storage provided by the caller.
main discards the result of the expression coroutine(). This means that it will manifest a temporary from the prvalue result object, and the type of this temporary will be eager. The temporary will be destroyed, but only after control returns to main.
Here's the tricky part: the return prvalue from get_result_object() is not eager. It's eager::promise::result_object. Initializing the return value requires performing an implicit conversion from result_object to eager. This requires manifesting a temporary of type result_object to perform that conversion on.
The standard rule for destroying temporaries is:
Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.
But... what is the "full-expression" here?
One would assume it would be the promise.get_result_object() expression. But is that a "full-expression" by C++'s rules? These rules are rather esoteric and technical. One could argue that promise.get_result_object() is being used to initialize an object and therefore it is de-facto an "init-declarator". But "init-declarator" is a piece of grammar, and promise.get_result_object() is not stated by the text to be an "init-declarator".
An argument could be made that the only expression that is certainly a full expression is coroutine(). And therefore, one could make the argument that any temporaries used to initialize the return value object should persist until control returns to the caller.
I would argue that the standard wording is under-specified and therefore both versions are equally legitimate until clarification is provided. Clang's version makes more sense (but not for the reasons you claim), but the others are at least arguable.
In the following C++20 program I put by mistake one extra pair of curved braces {} in B{{{{A{}}}}}:
#include <iostream>
struct A
{
A() { std::cout << "A() "; }
A( A&& ) = delete;
~A() { std::cout << "~A() "; }
};
struct B { std::initializer_list<A> l; };
int main()
{
[[maybe_unused]] auto x = B{{{{A{}}}}};
std::cout << ". ";
}
Clang rejected it, however with a strange error:
error: call to deleted constructor of 'const A'
But to my surprise GCC accepted it( https://gcc.godbolt.org/z/aPWe13xfc ).
Could you please explain why GCC accepts it (how does it treat extra curved braces)?
B{…}, since the single element of the initializer list is not designated and is not of type B (as it has no type at all), is aggregate initialization ([dcl.init.list]/3.4). B::l is thus copy-initialized from {{{A{}}}}; it's a specialization of std::initializer_list, so /3.6 and /5 apply. An "array of 1 const A" is created, and {{A{}}} is the initializer for its single element.
We can thus reduce the code to
const A a = {{A{}}};
with no mention of B at all, and indeed Clang and GCC produce the same disagreement for this line. Clang appears to be correct to reject it: that initialization is sent to constructors by /3.7, and obviously there is no constructor that could be viable (thus the error about the deleted move constructor).
Oddly, removing the extra pair of braces here (or in the original) causes both compilers to accept:
const A a = {A{}};
despite the fact that A is not an aggregate and so /3.7 still applies. Presumably both compilers are overenthusastically performing "guaranteed copy elision" (albeit to different degrees), identifying the prvalue A{} with an object eventually to be initialized by it; that, however, takes place only per [dcl.init.general]/16.6.1, which never comes into play in this analysis.
Is there any difference between the evaluation of expressions between the two return statements below, based on the extra parenthesis?
return a++ *(-b+123.456)/999.12344;
vs
return (a++ *(-b+123.456)/999.12344);
Programming Language C++
Standards CPP 98'ish (Before C++11)
Hope the question is clear. Expectation is to evaluate the expression in full.
Sloppy speaking x is the same as (x) (see this answer for the "striclty speaking" answer ;). Adding parentheses around the full expression does not change anything about operator precedence.
PS: It has an obscure impact on return value optimization (see this answer for the details). Though, it definitely has no impact on the value that is returned.
There are differences. Using parentheses will obviate return value optimisation.
If, for example, a and / or b were objects with all suitable operators overloaded, then using parentheses could suffer you the overhead of an object value copy.
For plain-old-data there is no difference, subject to the C++11 abomination
decltype(auto) ub_server() { int a; return (a);}
which actually gives you a dangling reference.
In summary: Don't use the enclosing parentheses unless you want some of the above behaviour, and possibly only then with a supporting comment.
Is there any difference between the evaluation of expressions between the two return statements below, based on the extra parenthesis?
No; the parentheses are completely redundant in this case.
An expression expr is actually not the same as an expression (expr), and you can observe this with return because copy/move elision is inhibited in the latter case:
#include <iostream>
struct T
{
T() { std::cout << "T()\n"; }
T(const T&) { std::cout << "T(const T&)\n"; }
T(T&&) { std::cout << "T(T&&)\n"; }
~T() { std::cout << "~T()\n"; }
};
T foo()
{
T t;
return t;
}
T bar()
{
T t;
return (t);
}
int main()
{
std::cout << "Test 1\n------\n";
foo();
std::cout << '\n';
std::cout << "Test 2\n------\n";
bar();
}
Output:
Test 1
------
T()
~T()
Test 2
------
T()
T(T&&)
~T()
~T()
(live demo)
You can probably observe the same result before C++17 because compilers have always tried to optimise return values. Even in your standard, C++98, you can probably observe that the copy constructor isn't invoked in the first case.
But, hey, none of that is relevant for a simple arithmetic expression.
I am writing a generic function like below.
template<class Iterator, class T>
void foo(Iterator first, Iterator last) {
T a;
cout << a << endl;
// do something with iterators
}
typedef vector<double>::iterator DblPtr;
vector<double> values;
foo< DblPtr, int>();
This functions prints out an undefined value for variable a, while if I change the initialization into
///
T a = T()
cout << a << endl;
// do something with iterators
I can see that the initialized value is 0 as I am expecting.
If I call T a the variable is initialized with the default value, but if i call T a = T() I believe that due to optimization the copy constructor should be called with the value of T() that is still the default one.
I cannot understand what is the difference behind these 2 lines and the reason why this happens?
First of all, default initiaization of built-in types such as int leaves them uninitialized. Value initialization leaves them zero-initialized. As for your example
This is a default initialization:
T a;
This is a value initialization, using copy initialization:
T a = T();
You are right that copies can be elided here, so this has the effect of creating a single value-initialized T object. However, it does require that T be copyable or move-copyable. This is the case with built-in types, but it is a restriction to bear in mind.
The copy initialization syntax is required because this is a function declaration:
T a();
but C++11 allows you to value-initialize like this:
T a{};
#include <iostream>
struct A
{
A() { std::cout << "(A::A)"; }
};
struct B
{
B() { std::cout << "(B::B)"; }
};
struct C
{
template<typename ...Args>
C(Args && ...) {}
};
int main(int agrc, char *argv[])
{
C {A(), B()}; // <-- prints (B::B)(A::A)
std::cout << std::endl;
C {(A(), B())}; // <-- prints (A::A)(B::B)
std::cout << std::endl;
return 0;
}
I've got 2 questions:
Why in first braced init list objects are created in right-to-left order?
Why parentheses in second case revert this order?
Edit: I've compiled it with msvs 2013
In the second instance, you're actually only initialising with B(); through the use of the comma operator, A() was constructed and thrown away first.
C {(A(), B())};
//|^^^^^^^^^^|
// \--------> (A(), B())
// ^^^ ^^^
// | |
// / \
// evaluated, THEN evaluated,
// discarded used
On the other hand, in the first instance, you're initialising the C from both temporaries through an initializer list, whose elements should also be evaluated left-to-right, as it happens, but your compiler is buggy in this regard:
[C++11: 8.5.4/4]: Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. —end note ]
I can reproduce the problem with GCC 4.8*, but Clang 3.5 behaves properly†. The bug has been discussed on the std-discussion list before‡, but I haven't found a GCC Bugzilla ID yet§.
C {A(), B()};
// ^^^ ^^^
// | \
// eval- THEN
// uated evaluated
// \ /
// \ /
// both used
* http://coliru.stacked-crooked.com/a/1f18e0d1f8973f3c
† http://coliru.stacked-crooked.com/a/5a6e7506e9be97c3
‡ https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/TQUnBFkUBDg
§ #51253 may be related.
Why in first braced init list objects are created in right-to-left order?
No. It is left-to-right. Your compiler has bug which is why it is evaluating right-to-left. GCC (4.8) is known to have this bug. Do you use GCC?
Why parentheses in second case revert this order?
Same. Left to right. In this case, comma operator comes into picture, which evaluates operands left-to-right.
It is an old bug of gcc 4.8.1 ( I suppose you use GCC) or of other compiler. I wrote about this bug several months ago
the initializer-list: a bug of GCC 4.8.1
Though it is written in Russian but you can translate it into English using for example google service translate.
As it was said by others elements of the initializer-list are evaluated from left to right and all side effects are applied before evaluation of the next element.
In your second code example you in fact call the constructor with one expression that is an expression of the comma operator. The comma operator in fact behaves the same way as initializer-lists that is it evaluates its operands from left to right and applies side effects before evaluation of the next operand.