std::initializer_list conversion in constructors - c++

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)

Related

Direct list initialization, which constructor will be used? [duplicate]

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);

Calling an explicit constructor with a braced-init list: ambiguous or not?

Consider the following:
struct A {
A(int, int) { }
};
struct B {
B(A ) { } // (1)
explicit B(int, int ) { } // (2)
};
int main() {
B paren({1, 2}); // (3)
B brace{1, 2}; // (4)
}
The construction of brace in (4) clearly and unambiguously calls (2). On clang, the construction of paren in (3) unambiguously calls (1) where as on gcc 5.2, it fails to compile with:
main.cpp: In function 'int main()':
main.cpp:11:19: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
B paren({1, 2});
^
main.cpp:6:5: note: candidate: B::B(A)
B(A ) { }
^
main.cpp:5:8: note: candidate: constexpr B::B(const B&)
struct B {
^
main.cpp:5:8: note: candidate: constexpr B::B(B&&)
Which compiler is right? I suspect clang is correct here, as the ambiguity in gcc can only arise through a path that involves implicitly constructing B{1,2} and passing that to the copy/move constructor - yet that constructor is marked explicit, so such implicit construction should not be allowed.
As far as I can tell, this is a clang bug.
Copy-list-initialization has a rather unintuitive behaviour: It considers explicit constructors as viable until overload resolution is completely finished, but can then reject the overload result if an explicit constructor is chosen. The wording in a post-N4567 draft, [over.match.list]p1
In copy-list-initialization, if an explicit constructor is chosen, the
initialization is ill-formed. [ Note: This differs from other
situations (13.3.1.3, 13.3.1.4), where only converting constructors
are considered for copy-initialization. This restriction only applies
if this initialization is part of the final result of overload
resolution. — end note ]
clang HEAD accepts the following program:
#include <iostream>
using namespace std;
struct String1 {
explicit String1(const char*) { cout << "String1\n"; }
};
struct String2 {
String2(const char*) { cout << "String2\n"; }
};
void f1(String1) { cout << "f1(String1)\n"; }
void f2(String2) { cout << "f2(String2)\n"; }
void f(String1) { cout << "f(String1)\n"; }
void f(String2) { cout << "f(String2)\n"; }
int main()
{
//f1( {"asdf"} );
f2( {"asdf"} );
f( {"asdf"} );
}
Which is, except for commenting out the call to f1, straight from Bjarne Stroustrup's N2532 - Uniform initialization, Chapter 4. Thanks to Johannes Schaub for showing me this paper on std-discussion.
The same chapter contains the following explanation:
The real advantage of explicit is that it renders f1("asdf") an
error. A problem is that overload resolution “prefers” non-explicit
constructors, so that f("asdf") calls f(String2). I consider the
resolution of f("asdf") less than ideal because the writer of
String2 probably didn’t mean to resolve ambiguities in favor of
String2 (at least not in every case where explicit and non-explicit
constructors occur like this) and the writer of String1 certainly
didn’t. The rule favors “sloppy programmers” who don’t use explicit.
For all I know, N2640 - Initializer Lists — Alternative Mechanism and Rationale is the last paper that includes rationale for this kind of overload resolution; it successor N2672 was voted into the C++11 draft.
From its chapter "The Meaning Of Explicit":
A first approach to make the example ill-formed is to require that all
constructors (explicit and non-explicit) are considered for implicit
conversions, but if an explicit constructor ends up being selected,
that program is ill-formed. This rule may introduce its own surprises;
for example:
struct Matrix {
explicit Matrix(int n, int n);
};
Matrix transpose(Matrix);
struct Pixel {
Pixel(int row, int col);
};
Pixel transpose(Pixel);
Pixel p = transpose({x, y}); // Error.
A second approach is to ignore the explicit constructors when looking
for the viability of an implicit conversion, but to include them when
actually selecting the converting constructor: If an explicit
constructor ends up being selected, the program is ill-formed. This
alternative approach allows the last (Pixel-vs-Matrix) example to work
as expected (transpose(Pixel) is selected), while making the
original example ("X x4 = { 10 };") ill-formed.
While this paper proposes to use the second approach, its wording seems to be flawed - in my interpretation of the wording, it doesn't produce the behaviour outlined in the rationale part of the paper. The wording is revised in N2672 to use the first approach, but I couldn't find any discussion about why this was changed.
There is of course slightly more wording involved in initializing a variable as in the OP, but considering the difference in behaviour between clang and gcc is the same for the first sample program in my answer, I think this covers the main points.
This is not a complete answer, even though it is too long as a comment.
I'll try to propose a counterexample to your reasoning and I'm ready to see downvote for I'm far from being sure.
Anyway, let's try!! :-)
It follows the reduced example:
struct A {
A(int, int) { }
};
struct B {
B(A) { }
explicit B(int, int ) { }
};
int main() {
B paren({1, 2});
}
In this case, the statement {1, 2} gives place apparently to two solutions:
direct initialization by means of B(A), because A(int, int) is not explicit and thus it is allowed and that's actually the first candidate
for the same reason above, it can be interpreted as B{B(A{1,2})} (well, let me abuse the notation to give you an idea and what I mean), that is {1,2} allows the construction of a B temporary object that is used immediately after as an argument for the copy/move constructor, and it's allowed again because the involved constructors are not explicit
The latter would explain the second and the third candidates.
Does it make sense?
I'm ready to delete the answers as long as you explain me what's wrong in my reasoning. :-)

What causes this constructor to delegate to itself when it takes an initializer list and delegates a vector?

#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.

In class initialization and initializer list

I have recently discovered that you cant have at the same time in class initialization and initializer list.
The following code fails :
struct s
{
int i=0;
};
int main() {
s s1; //s1.i = 0
//s s2={42}; //fails
return 0;
}
If I remove the in class initialization, the initializer list works fine !
Can someone explains me why a such thing is no allowed ?
In fact this is allowed in C++14.
struct s
{
int i=0;
};
int main() {
s s1;
s s2 = {42}; // succeeds
}
It's likely that your compiler just isn't implementing the new rule in C++14. The latest version of clang, however, accepts this and does the correct thing in C++14 mode.
When in-class initialization was added to C++11 it was specified such that it prevented a class from being an aggregate. This was done because at the time the aggregate concept was closely related to PoD types which need to be trivially constructible. Having an in-class initialization means that a type is no longer trivially constructible. Since then, however, the two concepts have become more independent, and so for C++14 a short proposal reversing that decision was accepted.
This initialization:
s s1 = { 42 };
requires that s be an aggregate, or that it have a valid constructor taking e.g an int or an std::initializer_list.
When you add a member initialization at the point of declaration, you render your class s a non-aggregate, so you can no longer use aggregate initialization.
You could use the same initialization syntax for your non-aggregate by adding a constructor:
struct s
{
s(int i) : i(i) {}
int i=0;
};
I believe this restriction has been relaxed for C++14.
See What are aggregates... for more information.

std::is_pod vs subclassing

Could someone please help me understand why the following code does not compile (g++ 4.8). My understanding is that one could initialize a POD
#include <iostream>
#include <type_traits>
struct my_int
{
int val_;
};
struct B : public my_int
{
};
int main()
{
std::cout << std::is_pod<my_int>::value << std::endl;
std::cout << std::is_pod<B>::value << std::endl;
const my_int v = { 123 };
//const B v2 = { 123 }; // does not compile with g++ 4.8.
return 0;
}
Compilation is:
g++ -std=c++11 t.cxx
t.cxx: In function 'int main()':
t.cxx:24:21: error: could not convert '{123}' from '<brace-enclosed initializer list>' to 'const B'
const B v = { 123 };
^
EDIT:
Thanks to everyone answer I now understand the concept of aggregate initialisation. I missed the fact that aggregate could not have base class. Therefore my current implementation plans needs to be changed. I wanted to do something like:
template < typename T >
struct base_class
{
int val_;
};
struct MyInt : public base_class<int>
{
void Func1() {}
};
struct MyDouble : public base_class<double>
{
void Func2() {}
};
I'll rework the above code to avoid the use of subclass to introduce special member functions, while avoid code duplication.
Disclaimer
Take the following with a grain of salt as it is my interpretation of things. I am by no means an expert. (Also I have some doubts about the aggregate - initializer list relation which I stated here.)
Answer
As far as I can tell this is not possible because the aggregate initialization of v2 would be applied to the non aggregate class type B.
From this answer you can take that aggregates must not have a base class,
which makes B a non aggregate and therefore not initializable by a brace enclosed initializer list.
On the other hand std::is_pod might not do what you think it does because the POD definition has changed in C++11. Therefore, it does not give you a hint if the type that is a POD can be initialized with such an aggregate initializer.
Addition
I am mainly discussing aggregate initialization here, but the more general term for this is list initialization which is less restrictive. However, checking all the cases I found in the linked resource there is no possibility to do list initialization either
because (following the resource's list of effects of an initializer list):
the initializer list is not empty
B is not an aggregate
B is not a specialization of std::initializer_list
B has no
constructor taking an initializer list
constructor fitting the signature of the list
B is no reference type
B cannot be copy-initialized from 123 and not direct-initialized because there is no constructor taking an int
B is not value-initialized because the initializer list ist not empty