#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.
Related
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.
According to [range.single.view#3], one of std::ranges::single_view constructors define as:
template<class... Args>
requires constructible_from<T, Args...>
constexpr explicit single_view(in_place_t, Args&&... args);
Effects: Initializes value_ as if by value_{in_place, std::forward<Args>(args)...}.
Why the standard specifies to use of direct-list-initialization ({}) to initialize value_? Why not use direct-initialization (()) just like std::optional, std::variant, and std::any?
Moreover, std::constructible_from(std::is_constructible) specifies that T obj(std::declval<Args>()...) is well-formed instead of T obj{std::declval<Args>()...}.
Consider the following:
ranges::single_view<std::vector<int>> sv(std::in_place, 100, 0);
std::cout << sv.begin()->size() << "\n"; // #1
std::optional<std::vector<int>> op(std::in_place, 100, 0);
std::cout << op->size() << "\n"; // #2
Because different initializations are used, #1 will call the std::vector<int>{0, 100} and print 2, and #2 will call the std::vector<int>(0, 100) and print 100.
Why does the standard specify using curly brace initialization for the underlying value even though it may cause inconsistencies? What is the consideration behind this?
value_ is a semiregular-box, so its constructors are well-known and don't include an initializer-list constructor. The inconsistency doesn't arise because in this case braces and parens are equivalent - the underlying type will always be constructed with parens, because that's what the semiregular-box's constructor is specified to do.
The behavior you are observing is a libstdc++ bug.
As to why the ranges clause uses list-initialization pervasively - there's no good reason, and it actually caused issues. LWG has approved a paper (P2367) that will remove misuses of list-initialization with normative impact; the remainder is tracked by editorial issue 4593.
#1 will call the std::vector{0, 100}
No, it won't.
value_ is of the type semiregular-box<T>, which is an exposition-only type that acts like optional<T>, but with some differences. But those differences don't apply here, so you can treat it as though it were just optional<T>.
An initializer_list can only be formed from a braced-init-list if all of the types of the expression are the same (or if they're convertible to some type without narrowing). But {in_place, std::forward<Args>(args)...} starts with the type std::in_place_t, which is almost certainly not one of the types of Args. So no initializer_list can be formed from it.
And even if a user does provide an in_place_t as Args (perhaps they're doing single_view<optional<T>> or something), it doesn't matter because optional (and therefore semiregular-box) doesn't have an initializer_list constructor. So it would call a matching regular constructor, just like if you'd used ().
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.
Is it possible to use aggregate initialization to make a pointer aptr point to a which is a member of the same struct ?
struct S {
int a;
int* aptr;
};
int main() {
S s = {
.a = 3,
.aptr = &a //point aptr to a
};
return 0;
}
The question is for both C and C++.
A working initialization would be:
struct S {
int a;
int* aptr;
};
int main() {
struct S s = {.a = 3, .aptr = &s.a};
printf("%d", *s.aptr);
}
Working samples:
C11 GNU
C++2a GNU
Regarding the correctness of the initialization:
For C:
The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.
For C++:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), 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.
However, despite the differences we can observe, the order in which the expressions are evaluated does not seem matter in this case, since you're not actually accessing the value of s.a, just its address which is accessible at this point.
So this is a correct initialization both for C and C++.
Something to note with this code, in MSVC, there is a compilation error in C++:
use of designated initializers requires at least '/std:c++latest'
Using std:c++latest the error changes to:
designated and non-designated initializers is nonstandard in C++
However, compilers that range from clang 3.1 to clang 10.0 and gcc 4.9.0 to gcc 10.0 with C++03 to C++2a compile fine with no warnings.
Designated initializers where introduced in C++20, so it is actually correct not to accept them, as MSVC still does not accept /std:c++20, it is not possible to use them yet, it also looks like gcc and clang always provided support for these initializers.
That being said, a second solution would be:
struct S {
int a;
int* aptr;
};
int main() {
struct S s = { 3, &s.a };
printf("%d", *s.aptr);
}
This second version of initialization compiles with no issues in every compiler tested, so it's fair to assume that it is more portable.
The first version is probably more easily readable and allows for a easier identification of errors in initialization, one of the advantages of designated initializers.
I was researching what is allowed in a core constant expression*, which is covered in section 5.19 Constant expressions paragraph 2 of the draft C++ standard which says:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function.—end note ]:
and lists out the exclusions in the bullets that follows and includes (emphasis mine):
— an operation that would have undefined behavior [ Note: including, for example, signed integer overflow (Clause 5), certain pointer arithmetic (5.7), division by zero (5.6), or certain shift operations (5.8) —end note ];
Huh? Why do constant expressions need this clause to cover undefined behavior? Is there something special about constant expressions that requires undefined behavior to have a special carve out in the exclusions?
Does having this clause give us any advantages or tools we would not have without it?
For reference, this looks like the last revision of the proposal for Generalized Constant Expressions.
The wording is actually the subject of defect report #1313 which says:
The requirements for constant expressions do not currently, but should, exclude expressions that have undefined behavior, such as pointer arithmetic when the pointers do not point to elements of the same array.
The resolution being the current wording we have now, so this clearly was intended, so what tools does this give us?
Let's see what happens when we try to create a constexpr variable with an expression that contains undefined behavior, we will use clang for all the following examples. This code (see it live):
constexpr int x = std::numeric_limits<int>::max() + 1 ;
produces the following error:
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = std::numeric_limits<int>::max() + 1 ;
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: value 2147483648 is outside the range of representable values of type 'int'
constexpr int x = std::numeric_limits<int>::max() + 1 ;
^
This code (see it live):
constexpr int x = 1 << 33 ; // Assuming 32-bit int
produces this error:
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = 1 << 33 ; // Assuming 32-bit int
^ ~~~~~~~
note: shift count 33 >= width of type 'int' (32 bits)
constexpr int x = 1 << 33 ; // Assuming 32-bit int
^
and this code which has undefined behavior in a constexpr function:
constexpr const char *str = "Hello World" ;
constexpr char access( int index )
{
return str[index] ;
}
int main()
{
constexpr char ch = access( 20 ) ;
}
produces this error:
error: constexpr variable 'ch' must be initialized by a constant expression
constexpr char ch = access( 20 ) ;
^ ~~~~~~~~~~~~
note: cannot refer to element 20 of array of 12 elements in a constant expression
return str[index] ;
^
Well that is useful the compiler can detect undefined behavior in constexpr, or at least what clang believes is undefined. Note, gcc behaves the same except in the case of undefined behavior with right and left shift, gcc will usually produce a warning in these cases but still sees the expression as constant.
We can use this functionality via SFINAE to detect whether an addition expression would cause overflow, the following contrived example was inspired by dyp's clever answer here:
#include <iostream>
#include <limits>
template <typename T1, typename T2>
struct addIsDefined
{
template <T1 t1, T2 t2>
static constexpr bool isDefined()
{
return isDefinedHelper<t1,t2>(0) ;
}
template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
static constexpr bool isDefinedHelper(int)
{
return true ;
}
template <T1 t1, T2 t2>
static constexpr bool isDefinedHelper(...)
{
return false ;
}
};
int main()
{
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}
which results in (see it live):
true
false
true
It is not evident that the standard requires this behavior but apparently this comment by Howard Hinnant indicates it indeed is:
[...] and is also constexpr, meaning UB is caught at compile time
Update
Somehow I missed Issue 695 Compile-time calculation errors in constexpr functions which revolves over the wording of section 5 paragraph 4 which used to say (emphasis mine going forward):
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (5.19 [expr.const]), in which case the program is ill-formed.
and goes on to say:
intended as an acceptable Standardese circumlocution for “evaluated at compile time,” a concept that is not directly defined by the Standard. It is not clear that this formulation adequately covers constexpr functions.
and a later note says:
[...]There is a tension between wanting to diagnose errors at compile time versus not diagnosing errors that will not actually occur at runtime.[...]The consensus of the CWG was that an expression like 1/0 should simply be considered non-constant; any diagnostic would result from the use of the expression in a context requiring a constant expression.
which if I am reading correctly confirms the intention was to be able to diagnose undefined behavior at compile time in the context requiring a constant expression.
We can not definitely say this was the intent but is does strongly suggest it was. The difference in how clang and gcc treat undefined shifts does leave some room for doubt.
I filed a gcc bug report: Right and left shift undefined behavior not an error in a constexpr. Although it seems like this is conforming, it does break SFINAE and we can see from my answer to Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr? that divergence in implementation observable to SFINAE users seems undesirable to the committee.
When we talk about undefined behavior, it is important to remember that the Standard leaves the behavior undefined for these cases. It does not prohibit implementations from making stronger guarantees. For example some implementations may guarantee that signed integer overflow wraps around, while others may guarantee saturation.
Requiring compilers to process constant expressions involving undefined behavior would limit the guarantees that an implementation could make, restricting them to producing some value without side effects (what the Standard calls indeterminate value). That excludes a lot of the extended guarantees found in the real world.
For example, some implementation or companion standard (i.e. POSIX) may define the behavior of integral division by zero to generate a signal. That's a side effect which would be lost if the expression were calculated at compile-time instead.
So, these expressions are rejected at compile-time to avoid loss of side effects in the execution environment.
There is another point to excluding undefined behavior from constant expressions: constant expressions should, by definition, be evaluated by the compiler at compile time. Allowing a constant expression to invoke undefined behavior would allow the compiler itself to show undefined behavior. And a compiler that formats your hard-drive because you compile some evil code is not something you want to have.