Consider the code
#include <iostream>
class Foo
{
int val_;
public:
Foo(std::initializer_list<Foo> il)
{
std::cout << "initializer_list ctor" << std::endl;
}
/* explicit */ Foo(int val): val_(val)
{
std::cout << "ctor" << std::endl;
};
};
int main(int argc, char const *argv[])
{
// why is the initializer_list ctor invoked?
Foo foo {10};
}
The output is
ctor
initializer_list ctor
As far as I understand, the value 10 is implicitly converted to a Foo (first ctor output), then the initializer constructor kicks in (second initializer_list ctor output). My question is why is this happening? Isn't the standard constructor Foo(int) a better match? I.e., I would have expected the output of this snippet to be just ctor.
PS: If I mark the constructor Foo(int) as explicit, then Foo(int) is the only constructor invoked, as the integer 10 cannot now be implicitly converted to a Foo.
§13.3.1.7 [over.match.list]/p1:
When objects of non-aggregate class type T are list-initialized
(8.5.4), overload resolution selects the constructor in two phases:
Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of
the initializer list as a single argument.
If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all
the constructors of the class T and the argument list consists of
the elements of the initializer list.
If the initializer list has no elements and T has a default
constructor, the first phase is omitted. In copy-list-initialization,
if an explicit constructor is chosen, the initialization is
ill-formed.
As long as there is a viable initializer-list constructor, it will trump all non-initializer-list constructors when list-initialization is used and the initializer list has at least one element.
The n2100 proposal for initializer lists goes into great detail about the decision to make sequence constructors (what they call constructors that take std::initializer_lists) to have priority over regular constructors. See Appendix B for a detailed discussion. It's succinctly summarized in the conclusion:
11.4 Conclusion
So, how do we decide between the remaining two alternatives (“ambiguity” and “sequence constructors take priority
over ordinary constructors)? Our proposal gives sequence constructors
priority because
Looking for ambiguities among all the constructors leads to too many “false positives”; that is, clashes between apparently unrelated
constructors. See examples below.
Disambiguation is itself error-prone (as well as verbose). See examples in §11.3.
Using exactly the same syntax for every number of elements of a homogeneous list is important – disambiguation should be done for
ordinary constructors (that do not have a regular pattern of
arguments). See examples in §11.3. The simplest example of a false
positive is the default constructor:
The simplest example of a false positive is the default constructor:
vector<int> v;
vector<int> v { }; // potentially ambiguous
void f(vector<int>&);
// ...
f({ }); // potentially ambiguous
It is possible to think of classes where initialization with no
members is semantically distinct from default initialization, but we
wouldn’t complicate the language to provide better support for those
cases than for the more common case where they are semantically the
same.
Giving priority to sequence constructors breaks argument checking into
more comprehensible chunks and gives better locality.
void f(const vector<double>&);
// ...
struct X { X(int); /* ... */ };
void f(X);
// ...
f(1); // call f(X); vector’s constructor is explicit
f({1}); // potentially ambiguous: X or vector?
f({1,2}); // potentially ambiguous: 1 or 2 elements of vector
Here, giving priority to sequence constructors eliminates the
interference from X. Picking X for f(1) is a variant of the problem
with explicit shown in §3.3.
The whole initializer list thing was meant to enable list initialisation like so:
std::vector<int> v { 0, 1, 2 };
Consider the case
std::vector<int> v { 123 };
That this initializes the vector with one element of value 123 rather than 123 elements of value zero is intended.
To access the other constructor, use the old syntax
Foo foo(10);
Related
In code like this:
#include <iostream>
#include <initializer_list>
#include <string>
struct A
{
A() { std::cout << "2" << std::endl; }
A(int a) { std::cout << "0" << std::endl; }
A(std::initializer_list<std::string> s) { std::cout << "3" << std::endl; }
A(std::initializer_list<int> l) { std::cout << "1" << std::endl; }
};
int main()
{
A a1{{}};
}
Why does it call std::initializer_list<int> specification of constructor?
It'll generate ambiguity compilation error if we define, for example, constructor with std::initializer_list<double>. What are the rules of such construction and why is it so specific about std::initializer_list with number as template argument?
If a class has an initializer list constructor, then {whatever goes here} means to pass {whatevergoeshere} as argument to the present constructors (if there are no initializer list constructors, then whatever goes here are passed as arguments).
So let's simplify the setting and ignore the other constructors, because apparently the compilers don't care about them
void f(std::initializer_list<std::string> s);
void f(std::initializer_list<int> l);
For f({{}}) we have this rule
Otherwise, if the parameter type is std::initializer_list and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor.
Here we have a single element {} and it needs a user defined conversion to initialize std::string and no conversion (identity) for int. Therefore, int is chosen.
For f({{{}}}) the element is {{}}. Can it be converted to int? The rule is
if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type
...
In all cases other than those enumerated above, no conversion is possible.
Can it be converted to std::string? Yes, because it has an initializer list constructor that has a std::initializer_list<char> init parameter. Therefore, std::string is chosen this time.
The difference to A a3({}) is that in such a case, it's not list initialization, but a "normal" initialization with a {} argument (note that one less nesting because of the missing outer braces). Here our two f-functions are called with {}. And since both lists have no elements, for both we have identity conversions and therefore an ambiguity.
The compiler in this case will also consider f(int) and get a tie with the other two functions. But a tie-breaker would apply that declares the int -parameter worse than the initializer_list parameters. So you have a partial order {int} < {initializer_list<string>, initializer_list<int>}, which is the reason for ambiguity, as the best group of conversion sequences does not contain a single candidate, but two.
{} to a scalar type (such as int, double, char*, etc.) is the identity conversion.
{} to a class type other than a specialization of std::initializer_list (e.g., std::string) is a user-defined conversion.
The former beats the latter.
I'm new to C++11 and I was wondering how this code works internally:
class MyClass
{
public:
MyClass(int a, double b) {
cout << "ctor()" << endl;
}
};
int main()
{
MyClass i1{4, 7};
return 0;
}
My understanding of the new initializer list is that it is a class std::initializer_list constructed by special syntax { .... } in the code. So how does this class instance created by {4, 7} internally get transformed to a form that fits the constructor to MyClass? Thanks.
I think this is how it happens. Extracted from: Explanation of list initialization at cppreference.com
If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all)
#include <initializer_list>
#include <vector>
struct test
{
using t = std::vector<test>;
test(t const &v)
{
}
test(t &&v)
{
}
test(std::initializer_list<test> v)
: test{t{v}} //error
{
}
};
Both Clang and GCC complain that the third constructor, the one taking the initializer list, delegates to itself. I don't understand how this is possible though, because you can't construct an initializer list from a vector.
It is trivial to fix the error by replacing the outer curly braces with round parenthesis, but why would this be an issue in the first place? This almost identical program compiles just fine:
#include <initializer_list>
struct a {};
struct b {};
struct test
{
test(a const &)
{
}
test(a &&)
{
}
test(std::initializer_list<b> v)
: test{a{}} //no error, still using curly braces
{
}
};
Interestingly, with the above second example, the error reappears if you substitute b with test. Can someone explain what is going on here?
The type of t{v} is std::vector<test>. The idea is that init-list constructors are always preferred wrt any other constructors, so test{t{v}} will first try to call an init-list constructor, if one exists, and if the types are compatible. In your case, this is possible, since test itself can be implicitly constructed from a std::vector<test> (via your first 2 constructors), so the compiler ends up delegating to the init-list constructor, hence the error.
In the second case, there is no ambiguity, since the type a{}is not implicitly convertible anymore to std::initializer_list<b>.
Make the constructors explicit in the first example, or call the base constructor with test(t{v}) instead, and your ambiguity will disappear (the compiler won't perform the implicit conversion anymore).
A simpler example (live here) that exhibits essentially the same behaviour as your first example is:
#include <initializer_list>
struct test
{
/*explicit*/ test(int){} // uncomment explicit and no more errors
test( std::initializer_list<test> v)
: test{42} {} // error, implicitly converts 42 to test(42) via the test(int)
};
int main(){}
The relevant part of the standard that deals with init-list constructors is §13.3.1.7/1 [over.match.list] - citation below taken from a now-deleted answer of #Praetorian -
When objects of non-aggregate class type T are list-initialized such that 8.5.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:
— Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
— If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements
of the initializer list.
I'm no C++ expert but the main difference is that
test{a{}}
Does not have an overload for initializer_list<a> so that constructor is not available.
On the other hand test{t{v}} does have an initializer_list<test> constructor available to it because you can create a test from vector. It can use the (I don't know the name of the rule) 1 cast transformation.
test{t{v}} -> test{test(t{v})}
The problem in your code is that you're writing a function calling itself.
test(std::initializer_list<test> v)
: test{t{v}} //error
{
}
test{t{v}} will first call the initializer-list of vector<test> with v as parameter. But vector will call the function again to initialize the value which will fail.
The compiler doesn't know how to resolve the problem. Changing the initialization to use parentheses will (as you said) fix this because it will then call an implicit copy constructor in the vector (doing nothing because the your structure isn't doing anything).
The second example starts with this call: a{}
It is resolved fine because a is a basic structure with implicitly defined constructors. The second call is the test{...} one
Because the type is a and there is a constructor for a it runs just fine.
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.
Is there a rule of thumb to decide when to use the old syntax () instead of the new syntax {}?
To initialize a struct:
struct myclass
{
myclass(int px, int py) : x(px), y(py) {}
private:
int x, y;
};
...
myclass object{0, 0};
Now in the case of a vector for example, it has many constructors. Whenever I do the following:
vector<double> numbers{10};
I get a vector of 1 element instead of one with 10 elements as one of the constructors is:
explicit vector ( size_type n, const T& value= T(), const Allocator& = Allocator() );
My suspicion is that whenever a class defines an initializer list constructor as in the case of a vector, it gets called with the {} syntax.
So, is what I am thinking correct. i.e. Should I revert to the old syntax only whenever a class defines an initializer list constructor to call a different constructor? e.g. to correct the above code:
vector<double> numbers(10); // 10 elements instead of just one element with value=10
I've found the answer in the standard docs(latest draft). Hopefully, I'll try to explain what I understood.
First, if a class defines an initialization list constructor, then it is used whenever suitable:
§ 8.5.4 (page 203)
Initializer-list constructors are
favored over other constructors in
list-initialization (13.3.1.7).
I think this is a great feature to have, eliminating the headache associated with the non-uniform style :)
Anyway, the only gotcha(which my question is about) is that if you design a class without the initializer constructor, then you add it later you may get surprising result.
Basically, imagine std::vector didn't have the initializer list constructor, then the following would create a vector with 10 elements:
std::vector<int> numbers{10};
By adding the initializer list constructor, the compiler would favor it over the other constructor because of the {} syntax. This behavior would happen because the elements of the init-list {10} are accepted using the init-list constructor. If there is no acceptable conversion, any other constructor shall be used e.g.:
std::vector<string> vec{10};
// a vector of 10 elements.
// the usual constructor got used because "{0}"
// is not accepted as an init-list of type string.
Take a look at this:
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=453&rll=1
The use of a {}-style initializers on a variable has no direct mapping to the initialization lists on any constructors of the class. Those constructor initialization lists can be added/removed/modified without breaking existing callers.
Basically the different behavior of the container is special, and requires special code in that container, specifically a constructor taking a std::initializer_list. For POD and simple objects, you can use {} and () interchangeably.