How generic is the {} construction syntax? - c++

In a talk about initialization lists, I understood Stroustrup basically saying that the new construction syntax with curly braces is supposed to be a general replacement for all the previous construction syntaxes:
X x1(); // most vexing parse ... doesn't work as intended
X x2(x1);
X x3 = x1;
X x4 = X();
Instead, the new syntax is supposed to be used uniformly as a possible replacement that you can use in every situation ... again, that's the core message that I took from his talk. Maybe I misunderstood him.
So, the question is, how generic is this syntax? Is it possible to never use old-style construction in new C++11 code or are there situations where you have to revert?
This question was triggered/motivated when I encountered the following error, which I believe is an error in the compiler (but I'd be happy to be corrected).
struct X {};
int main() {
X x;
X& y{x}; // works with (x)
X& z{y}; // works with (y)
}
Which doesn't compile on g++ 4.7.1 nor does it on ideone's 4.5.1.
prog.cpp: In function 'int main()':
prog.cpp:5:9: error: invalid initialization of non-const reference of type 'X&' from an rvalue of type '<brace-enclosed initializer list>'
prog.cpp:6:9: error: invalid initialization of non-const reference of type 'X&' from an rvalue of type '<brace-enclosed initializer list>'
Note that it works when I replace X with int.

Brace initialization works everywhere an initializer is used. There are situations where you have to use parens in order to access a constructor that a brace initializer cannot access, but they are rare.
std::vector<int> v(1,1);
std::vector<int> v{1,1};
vector<int> happens to have a specialized constructor that takes two ints and is therefore ambiguous with trying to construct a vector two ints long. The ambiguous constructor exists only for backwards compatibility. New classes should not be defined with any constructors that would conflict with an initializer_list.
The ambiguity is resolved by the fact that brace initialization syntax prefers initializer_list constructors over other constructors that would otherwise match. If you want to resolve the ambiguity in favor of using the non-initializer_list constructor then you can't use brace initialization.
Bjarne Stroustrup writes
The uniform use of {} initialization only became possible in C++11, so older C++ code uses () and = initialization. Consequently, the () and = may be more familiar to you. However, I don't know any logical reason to prefer the () notation except in the rare case where you need to distinguish between initialization with a list of elements and a list of constructor arguments.
— The C++ Programming Language, Fourth Edition §17.3.2.1
Your example code is perfectly legal and should work as expected. The error you're getting is simply a bug in GCC. Clang and VC++ 2012 both accept the code.

Related

Inline use of non-default explicit constructor for a member object

In C++11 (or future), is there some simple variation of the following which is legal?
class A
{
public:
std::vector<char> b(123); // declare a vector with 123 elements
};
The closest I can find is a bit clunky, and maybe inefficient...
class A
{
public:
std::vector<char> b = std::vector<char>(123);
};
I'm trying to avoid using an initializer list. I prefer to consolidate the declaration and initialization of b into a single line of code. The vector will always be the same size.
I'm using std::vector in this example, but presumably the answer would be more generally applicable.
For good measure, here's the error message from gcc version 4.8:
error: expected identifier before numeric constant
std::vector b(123);
and here's the message from clang version 3.7:
error: expected parameter declarator
std::vector b(123);
Highly unlikely. The original proposal to allow NSDMIs in the first place addresses this issue:
N2756
An issue raised in Kona regarding scope of identifiers:
During discussion in the Core Working Group at the September ’07
meeting in Kona, a question arose about the scope of identifiers in
the initializer. Do we want to allow class scope with the possibility
of forward lookup; or do we want to require that the initializers be
well-defined at the point that they’re parsed?
What’s desired:
The motivation for class-scope lookup is that we’d like to be able to
put anything in a non-static data member’s initializer that we could
put in a mem-initializer without significantly changing the semantics
(modulo direct initialization vs. copy initialization):
int x();
struct S {
int i;
S() : i(x()) {} // currently well-formed, uses S::x()
// ...
static int x();
};
struct T {
int i = x(); // should use T::x(), ::x() would be a surprise
// ...
static int x();
};
Problem 1:
Unfortunately, this makes initializers of the “( expression-list )”
form ambiguous at the time that the declaration is being parsed:
...
The proposal:
CWG had a 6-to-3 straw poll in Kona in favor of class-scope lookup;
and that is what this paper proposes, with initializers for non-static
data members limited to the “= initializer-clause” and “{
initializer-list }” forms. We believe:
Problem 1: This problem does not occur as we don’t propose the ()
notation. The = and {} initializer notations do not suffer from this
problem.
There is nothing inefficient about the clunky way of initialization unless your compiler doesn't employ copy elision (and all major ones do). The issue is that the language designers of C++ have backed themselves into a corner. Because initializer list constructors are greedy, brace initialization will construct a vector with the given elements, while the older syntax using parentheses calls the explicit constructor to set a size.
Except you can't use that constructor in a NSDMI. Unless you use an equals sign.
If for some reason that bothers you, there are some clunky workarounds:
std::vector<char> c = decltype(c)(123);
// ...
using VChar = std::vector<char>;
VChar v = VChar(123);
Or realize that new features do not preclude existing features:
std::vector<char> c;
A() : c(123)
{
}

Wrong std::vector constructor

I'm getting a strange error from Clang when compiling what should be a straightforward line of code.
This code produces an error:
size_t s = 5;
std::vector<double> bestScores{s, -1.0};
I'm simply trying to use constructor #2 to fill a new vector with five -1.0 values. The error I get is Non-constant expression cannot be narrowed from type 'size_type' (aka 'unsigned long') to 'double' in initializer list.
What is going on? This compiles fine:
std::vector<double> bestScores{5, -1.0};
Is it trying to use the initializer list constructor? I thought you needed two curly braces for that:
std::vector<double> bestScores{{5, -1.0}};
The issue is that you are constructing the vector using a brace-enclosed initialization list. That favours the std::initializer_list<T> constructor when applicable. In this case, the size_t, -1.0 list is compatible with std::initializer_list<double>, so that constructor gets picked. You need to use the old-style, C++03 construction:
std::vector<double> bestScores(s, -1.0);
This is one of the gotchas of brace-enclosed initializers. They don't play well for certain standard library container instantiations. You have to remember that the std::initializer_list constructor will trump the others.
The issue is that when a class has an std::initializer_list constructor, it will prefer that when using the uniform initialization syntax if the arguments are convertible to the initializer_list's type (double in this case). See a detailed answer at programmers.stackexchange.com.
For now, your solution is to use the non-uniform syntax that uses parenthesis. This means it won't consider the std::initializer_list constructor, and do what you want in this case.
std::vector<double> bestScores(s, -1.0)

Initializing class using { * this }

It was suggested by a team member that using an intializer like this:
return Demo{ *this };
was better than:
return Demo(*this);
Assuming a simple class like this:
class Demo {
public:
int value1;
Demo(){}
Demo(Demo& demo) {
this->value1 = demo.value1;
}
Demo Clone() {
return Demo{ *this };
}
};
I admit to having not seen the { *this } syntax before, and couldn't find a reference that explained it well enough that I understood how the two options differed. Is there a performance benefit, a syntax choice, or something more?
Your colleague is missing a trick with "uniform initialization", there is no need for the type-name when it is known. E.g. when creating a return value. Clone could be defined as:
Demo Clone() {
return {*this};
}
This will call the Demo copy constructor as needed. Whether you think this is better or not, is up to you.
In GOTW 1 Sutter states as a guideline:
Guideline: Prefer to use initialization with { }, such as vector v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine. …
In particular, using braces can avoid confusion with:
Demo d(); //function declaration, but looks like it might construct a Demo
Demo d{}; //constructs a Demo, as you'd expect
The brace syntax will use a constructor that takes an initializer list first, if one exists. Otherwise it will use a normal constructor. It also prevents the chance of the vexing parse listed above.
There is also different behaviour when using copy initialization. With the standard way
Demo d = x;
The compiler has the option to convert x to a Demo if necessary and then move/copy the converted r-value into w. Something similar to Demo d(Demo(x)); meaning that more than one constructor is called.
Demo d = {x};
This is equivalent to Demo d{x} and guarantees that only one constructor will be called. With both assignments above explicit constructors are cannot be used.
As mentioned in the comments, there are some pitfalls. With classes that take an initializer_list and have "normal" constructors can cause confusion.
vector<int> v{5}; // vector containing one element of '5'
vector<int> v(5); // vector containing five elements.
This is just another syntax for calling your copy constructor (actually, for calling a constructor taking what is in the braces as parameters, in this case, your copy constructor).
Personally, I would say it's worse than before simply because it does the same... it just relies on C++ 11. So it adds dependencies without benefits. But your mileage may vary. You will have to ask your colleague.
I must admit that I'd never seen that before.
WikiPedia says this about C++11 initializer lists (search for "Uniform initialization"):
C++03 has a number of problems with initializing types. There are several ways to initialize types, and they do not all produce the same results when interchanged. The traditional constructor syntax, for example, can look like a function declaration, and steps must be taken to ensure that the compiler's most vexing parse rule will not mistake it for such. Only aggregates and POD types can be initialized with aggregate initializers (using SomeType var = {/stuff/};).
Then, later, they have this example,
BasicStruct var1{5, 3.2}; // C type struct, containing only POD
AltStruct var2{2, 4.3}; // C++ class, with constructors, not
// necessarily POD members
with the following explanation:
The initialization of var1 behaves exactly as though it were aggregate-initialization. That is, each data member of an object, in turn, will be copy-initialized with the corresponding value from the initializer-list. Implicit type conversion will be used where necessary. If no conversion exists, or only a narrowing conversion exists, the program is ill-formed. The initialization of var2 invokes the constructor.
They also have further examples for the case where initialiser list constructors were provided.
So based on the above alone: For the plain-old-data struct case, I don't know if there is any advantage. For a C++11 class, using the {} syntax may help avoid those pesky scenarios where the compiler thinks you're declaring a function. Maybe that is the advantage your colleague was referring to?
Sorry for comming late to this discussion but I want to add some points about the different types of initialization not mentioned by others.
Consider:
struct foo {
foo(int) {}
};
foo f() {
// Suppose we have either:
//return 1; // #1
//return {1}; // #2
//return foo(1); // #3
//return foo{1}; // #4
}
Then,
#1, #3 and #4 might call the copy/move constructor (if RVO isn't performed) whereas #2 won't call the copy/move constructor.
Notice that the most popular compilers do perform RVO and thus, in practice, all return statements above are equivalent. However, even when RVO is performed a copy/move constructor must be available (must be accessible to f and defined but not as deleted) for #1, #3 and #4 otherwise the compiler/linker will raise an error.
Suppose now that the constructor is explicit:
struct foo {
explicit foo(int) {}
};
Then,
#1 and #2 don't compile whereas #3 and #4 do compile.
Finally, if the constructor is explicit and no copy/move constructor is available:
struct foo {
explicit foo(int) {}
foo(const foo&) = delete;
};
none of the return statements compile/link.
This is known as list-initialization. The idea is that in C++11, you will have uniform initialization across the board, and avoid ambiguity where the compiler might think you may be making a function declaration (also known as a vexing parse). A small example:
vec3 GetValue()
{
return {x, y, z}; // normally vec(x, y, z)
}
One reason you would want to avoid list-initialization is where your class takes an initializer list constructor that does something different than you would expect.

Constructor and initializer_list

Could somebody tell me the theory behind this?
Why the last call doesn't compile?
test.cc: In function ‘int main()’: test.cc:15:12: error: too many braces around initializer for ‘int’ [-fpermissive] test.cc:15:12:
error: invalid conversion from ‘’ to ‘int’ [-fpermissive] test.cc:9:6: error: initializing argument 1 of ‘void f(std::initializer_list)’ [-fpermissive] test.cc:15:12:
error: aggregate value used where an integer was expected
I think either c++11 or g++ 4.7 is broken on this.
Thank you!
#include <initializer_list>
class A {
public:
A(const std::initializer_list<int>) {}
};
void f(const std::initializer_list<int>) {}
int main() {
A({1}); // Compile OK
f({1}); // Compile OK
A({{{1}}}); // Compile OK
//f({{{1}}}); // Compile Error.
}
Here is what I believe GCC is thinking.
This is your program with 1 extra line, and the interesting lines numbered.
int main() {
A({1}); // 1. Compile OK
f({1}); // 2. Compile OK
A{{1}}; // 3. Compile OK, equivalent to 1.
A({{{1}}}); // 4. Compile OK
//f({{{1}}}); // 5. Compile Error.
}
Why does GCC compile 4 but not 5?
For clarity, suppose the construction at #4 actually declared something:
A a{{{1}}}; // 4a. Compile OK
GCC asks if the argument of the constructor, which is {{1}} is
implicitly convertible to A. So is:
A{{1}}
a valid conversion from {{1}} to A? Yes it is - as per 3.
This reasoning, of course, is not applicable to #5; hence error.
If you want to stop GCC from accepting #4, then block the
enabling conversion by making the enabling constructor explicit:
class A {
public:
explicit A(const std::initializer_list<int> il) {}
};
Then #4 will give the error:
error: converting to ‘A’ from initializer list would use explicit constructor ‘A::A(std::initializer_list<int>)’
A {1} can initialize an int. A {{1}} probably should not - there is a defect report on tbe committee for that. GCC forbids that and clang just tends to emit warnings about redundant braces currently.
When X is a class that has copy or move constructors, then X ({...}) may invoke invoke one of them. Notice that X {...} may too, but is restricted to not allow user defined conversions (for a copy or move constructor).
Now with your A ({{{1}}}) the first brace is consumed by the copy/move constructor. The second goes to the initializer list recursively. And the third goes to the contained int.
According to the Standard, adding one more brace will break for A ({{{{1}}}}). Because the second brace would need to be consumed by a copy/move constructor of A but need a user defined conversion sequence. The same holds for A {{{{1}}}}, which is invalid for this reason too.

Strange case of C++11 overload resolution

I came across a rather strange case of overload resolution today. I reduced it to the following:
struct S
{
S(int, int = 0);
};
class C
{
public:
template <typename... Args>
C(S, Args... args);
C(const C&) = delete;
};
int main()
{
C c({1, 2});
}
I fully expected C c({1, 2}) to match the first constructor of C, with the number of variadic arguments being zero, and {1, 2} being treated as an initializer list construction of an S object.
However, I get a compiler error that indicates that instead, it matches the deleted copy constructor of C!
test.cpp: In function 'int main()':
test.cpp:17:15: error: use of deleted function 'C(const C &)'
test.cpp:12:5: error: declared here
I can sort of see how that might work - {1, 2} can be construed as a valid initializer for C, with the 1 being an initializer for the S (which is implicitly constructible from an int because the second argument of its constructor has a default value), and the 2 being a variadic argument... but I don't see why that would be a better match, especially seeing as the copy constructor in question is deleted.
Could someone please explain the overload resolution rules that are in play here, and say whether there is a workaround that does not involve mentioning the name of S in the constructor call?
EDIT: Since someone mentioned the snippet compiles with a different compiler, I should clarify that I got the above error with GCC 4.6.1.
EDIT 2: I simplified the snippet even further to get an even more disturbing failure:
struct S
{
S(int, int = 0);
};
struct C
{
C(S);
};
int main()
{
C c({1});
}
Errors:
test.cpp: In function 'int main()':
test.cpp:13:12: error: call of overloaded 'C(<brace-enclosed initializer list>)' is ambiguous
test.cpp:13:12: note: candidates are:
test.cpp:8:5: note: C::C(S)
test.cpp:6:8: note: constexpr C::C(const C&)
test.cpp:6:8: note: constexpr C::C(C&&)
And this time, GCC 4.5.1 gives the same error, too (minus the constexprs and the move constructor which it does not generate implicitly).
I find it very hard to believe that this is what the language designers intended...
For C c({1, 2}); you have two constructors that can be used. So overload resolution takes place and looks what function to take
C(S, Args...)
C(const C&)
Args will have been deduced to zero as you figured out. So the compiler compares constructing S against constructing a C temporary out of {1, 2}. Constructing S from {1, 2} is straight forward and takes your declared constructor of S. Constructing C from {1, 2} also is straight forward and takes your constructor template (the copy constructor is not viable because it has only one parameter, but two arguments - 1 and 2 - are passed). These two conversion sequences are not comparable. So the two constructors would be ambiguous, if it weren't for the fact that the first is a template. So GCC will prefer the non-template, selecting the deleted copy constructor and will give you a diagnostic.
Now for your C c({1}); testcase, three constructors can be used
C(S)
C(C const&)
C(C &&)
For the two last, the compiler will prefer the third because it binds an rvalue to an rvalue. But if you consider C(S) against C(C&&) you won't find a winner between the two parameter types because for C(S) you can construct an S from a {1} and for C(C&&) you can initialize a C temporary from a {1} by taking the C(S) constructor (the Standard explicitly forbids user defined conversions for a parameter of a move or copy constructor to be usable for an initialization of a class C object from {...}, since this could result in unwanted ambiguities; this is why the conversion of 1 to C&& is not considered here but only the conversion from 1 to S). But this time, as opposed to your first testcase, neither constructor is a template so you end up with an ambiguity.
This is entirely how things are intended to work. Initialization in C++ is weird so getting everything "intuitive" to everyone is going to be impossible sadly. Even a simple example as above quickly gets complicated. When I wrote this answer and after an hour I looked at it again by accident I noticed I overlooked something and had to fix the answer.
You might be correct in your interpretation of why it can create a C from that initializer list. ideone happily compiles your example code, and both compilers can't be correct. Assuming creating the copy is valid, however...
So from the compiler's point of view, it has two choices: Create a new S{1,2} and use the templated constructor, or create a new C{1,2} and use the copy constructor. As a rule, non-template functions are preferred over template ones, so the copy constructor is chosen. Then it looks at whether or not the function can be called... it can't, so it spits out an error.
SFINAE requires a different type of error... they occur during the first step, when checking to see which functions are possible matches. If simply creating the function results in an error, that error is ignored, and the function not considered as a possible overload. After the possible overloads are enumerated, this error suppression is turned off and you're stuck with what you get.