Overload resolution between two constructors from std::initializer_list - c++

In following program, struct C has two constructors : one from std::initializer_list<A> and the other from std::initializer_list<B>. Then an object of the struct is created with C{{1}}:
#include <initializer_list>
struct A {
int i;
};
struct B {
constexpr explicit B(int) {}
};
struct C {
int v;
constexpr C(std::initializer_list<A>) : v(1) {}
constexpr C(std::initializer_list<B>) : v(2) {}
};
static_assert( C{{1}}.v == 1 );
Since only aggregate A can be implicitly constructed from int, one could expect that C(std::initializer_list<A>) is preferred and the program succeeds. And indeed it does in Clang.
However GCC complains:
error: call of overloaded 'C(<brace-enclosed initializer list>)' is ambiguous
note: candidate: 'constexpr C::C(std::initializer_list<B>)'
note: candidate: 'constexpr C::C(std::initializer_list<A>)'
and so does MSVC:
error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'C'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
Demo: https://gcc.godbolt.org/z/joz91q4ed
Which compiler is correct here?

The wording could be clearer (which is unsurprising), but GCC and MSVC are correct here: the relevant rule ([over.ics.list]/7) checks only that
overload resolution […] chooses a single best constructor […] to perform the initialization of an object of type X from the argument initializer list
so the fact that the initialization of B from {1} would be ill-formed is irrelevant.
There are several such places where implicit conversion sequences are more liberal than actual initialization, causing certain cases to be ambiguous even though some of the possibilities wouldn’t actually work. If the programmer was confused and thought one of those near misses was actually a better match, it’s a feature that the ambiguity is reported.

Related

Uniform vs legacy initialisation producing different compilation results

I alighted on this while permuting with a trivial piece of code:
struct Base0 {};
struct Base1 {};
template<typename... Ts>
struct Derived: Ts... {};
int main() {
Derived<Base0, Base1> d0 {Base0{}, Base1{}}; // OK
Derived<Base0, Base1> d1 (Base0{}, Base1{}); // ERROR
}
I thought both d0 and d1 should have resulted in a compilation error since I can't see how Derived without any matching ctor takes ctor arguments as passed and flags d0's compilation as fine.
There's probably something obvious I'm missing. What is it about the uniform initialisation that's making it pass ? Is it aggregate initialisation or something ? What's happening with the temporaries passed to the ctor ?
Using C++17 online compiler here
Edit
As asked, I'm providing a copy-paste of the spew-out:
main.cpp: In function ‘int main()’:
main.cpp:9:47: error: no matching function for call to ‘Derived::Derived(Base0, Base1)’
Derived<Base0, Base1> d1 (Base0{}, Base1{}); // ERROR
^
main.cpp:5:8: note: candidate: constexpr Derived::Derived()
struct Derived: Ts... {};
^~~~~~~
main.cpp:5:8: note: candidate expects 0 arguments, 2 provided
main.cpp:5:8: note: candidate: constexpr Derived::Derived(const Derived&)
main.cpp:5:8: note: candidate expects 1 argument, 2 provided
main.cpp:5:8: note: candidate: constexpr Derived::Derived(Derived&&)
main.cpp:5:8: note: candidate expects 1 argument, 2 provided
Looks like this is a new C++17 feature of aggregate initialisation:
Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.
It comes with the change that a class with bases may now be an aggregate (as long as they are not virtual, private, or protected… though they don't even need to be aggregates! 😲).
Your failing case does not use aggregate initialisation, but instead attempts a good old-fashioned constructor invocation. As you've identified, no such constructor exists.

Curly braces constructor prefers initializer_list over better match. Why?

#include <vector>
using std::size_t;
struct Foo
{
Foo(size_t i, char c) {}
};
Foo Bar1()
{
size_t i = 0;
char c = 'x';
return { i, c }; // good
}
std::vector<char> Bar2()
{
size_t i = 0;
char c = 'x';
return { i, c }; // bad
}
https://wandbox.org/permlink/87uD1ikpMkThPTaw
warning: narrowing conversion of 'i' from 'std::size_t {aka long
unsigned int}' to 'char' inside { }
Obviously it tries to use the initializer_list of vector. But why doesn't it use the better match vector<char>(size_t, char) ?
Can i use the desired constructor in a return statement without writing the type again?
Because initializer_list constructors, if at all possible, take precedence over other constructors. This is to make edge cases less confusing - specifically, this particular vector constructor that you expect it to use was deemed too easily selected by accident.
Specifically, the standard says in 16.3.1.7 "Initialization by list-initialization" [over.match.list] (latest draft, N4687):
(1) When objects of non-aggregate class type T are list-initialized such that 11.6.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 (11.6.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.
So if you do std::vector<char>( i, c ), this section does not apply at all, since it isn't list-initialization. Normal overload resolution is applied, the (size_t, char) constructor is found, and used.
But if you do std::vector<char>{ i, c }, this is list-initialization. The initializer list constructors are tried first, and the (initializer_list<char>) constructor is a match (even though it involves the narrowing conversion from size_t to char), so it is used before the size+value constructor is ever considered.
So to answer the edited-in question: no, you can't create the vector without naming its type. But in C++17 you can use class template argument deduction and simply write return std::vector(i, c);

Explicit constructors and nested initializer lists

The following code successfully compiles with most modern C++11 compatible compilers (GCC >= 5.x, Clang, ICC, MSVC).
#include <string>
struct A
{
explicit A(const char *) {}
A(std::string) {}
};
struct B
{
B(A) {}
B(B &) = delete;
};
int main( void )
{
B b1({{{"test"}}});
}
But why does it compile in the first place, and how are the listed compilers interpreting that code?
Why is MSVC able to compile this without B(B &) = delete;, but the other 3 compilers all need it?
And why does it fail in all compilers except MSVC when I delete a different signature of the copy constructor, e.g. B(const B &) = delete;?
Are the compilers even all choosing the same constructors?
Why does Clang emit the following warning?
17 : <source>:17:16: warning: braces around scalar initializer [-Wbraced-scalar-init]
B b1({{{"test"}}});
Instead of explaining the behavior of compilers, I'll try to explain what the standard says.
Primary Example
To direct-initialize b1 from {{{"test"}}}, overload resolution applies to choose the best constructor of B. Because there is no implicit conversion from {{{"test"}}} to B& (list initializer is not a lvalue), the constructor B(B&) is not viable. We then focus on the constructor B(A), and check whether it is viable.
To determine the implicit conversion sequence from {{{"test"}}} to A (I will use the notation {{{"test"}}} -> A for simplicity), overload resolution applies to choose the best constructor of A, so we need to compare {{"test"}} -> const char* and {{"test"}} -> std::string (note the outermost layer of braces is elided) according to [over.match.list]/1:
When objects of non-aggregate class type T are list-initialized such that [dcl.init.list] specifies that overload resolution is performed according to the rules in this subclause, overload resolution selects the constructor in two phases:
Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T...
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.
... In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.
Note all constructors are considered here regardless of the specifier explicit.
{{"test"}} -> const char* does not exist according to [over.ics.list]/10 and [over.ics.list]/11:
Otherwise, if the parameter type is not a class:
if the initializer list has one element that is not itself an initializer list...
if the initializer list has no elements...
In all cases other than those enumerated above, no conversion is possible.
To determine {{"test"}} -> std::string, the same process is taken, and overload resolution chooses the constructor of std::string that takes a parameter of type const char*.
As a result, {{{"test"}}} -> A is done by choosing the constructor A(std::string).
Variations
What if explicit is removed?
The process does not change. GCC will choose the constructor A(const char*) while Clang will choose the constructor A(std::string). I think it is a bug for GCC.
What if there are only two layers of braces in the initializer of b1?
Note {{"test"}} -> const char* does not exist but {"test"} -> const char* exists. So if there are only two layers of braces in the initializer of b1, the constructor A(const char*) is chosen because {"test"} -> const char* is better than {"test"} -> std::string. As a result, an explicit constructor is chosen in copy-list-initialization (initialization of the parameter A in the constructor B(A) from {"test"}), then the program is ill-formed.
What if the constructor B(const B&) is declared?
Note this also happens if the declaration of B(B&) is removed. This time we need to compare {{{"test"}}} -> A and {{{"test"}}} -> const B&, or {{{"test"}}} -> const B equivalently.
To determine {{{"test"}}} -> const B, the process described above is taken. We need to compare {{"test"}} -> A and {{"test"}} -> const B&. Note {{"test"}} -> const B& does not exist according to [over.best.ics]/4:
However, if the target is
— the first parameter of a constructor or
— the implicit object parameter of a user-defined conversion function
and the constructor or user-defined conversion function is a candidate by
— [over.match.ctor], when the argument is the temporary in the second step of a class copy-initialization,
— [over.match.copy], [over.match.conv], or [over.match.ref] (in all cases), or
— the second phase of [over.match.list] when the initializer list has exactly one element that is itself an initializer list, and the target is the first parameter of a constructor of class X, and the conversion is to X or
reference to cv X,
user-defined conversion sequences are not considered.
To determine {{"test"}} -> A, the process described above is taken again. This is almost the same as the case we talked in the previous subsection. As a result, the constructor A(const char*) is chosen. Note the constructor is chosen here to determine {{{"test"}}} -> const B, and does not apply actually. This is permitted though the constructor is explicit.
As a result, {{{"test"}}} -> const B is done by choosing the constructor B(A), then the constructor A(const char*). Now both {{{"test"}}} -> A and {{{"test"}}} -> const B are user-defined conversion sequences and neither is better than the other, so the initialization of b1 is ambiguous.
What if the parentheses is replaced by braces?
According to [over.best.ics]/4, which is block-quoted in the previous subsection, the user defined conversion {{{"test"}}} -> const B& is not considered. So the result is the same as the primary example even if the constructor B(const B&) is declared.
B b1({{{"test"}}}); is like B b1(A{std::string{const char*[1]{"test"}}});
16.3.3.1.5 List-initialization sequence [over.ics.list]
4 Otherwise, if the parameter type is a character array 133 and the initializer list has a single element that is an appropriately-typed string literal (11.6.2), the implicit conversion sequence is the identity conversion.
And the compiler tries all possible implicit conversions. For example if we have class C with the following constructors:
#include <string>
struct C
{
template<typename T, size_t N> C(const T* (&&) [N]) {}
template<typename T, size_t N> C(const T (&&) [N]) {}
template<typename T=char> C(const T* (&&)) {}
template<typename T=char> C(std::initializer_list<char>&&) {}
};
struct A
{
explicit A(const char *) {}
A(C ) {}
};
struct B
{
B(A) {}
B(B &) = delete;
};
int main( void )
{
const char* p{"test"};
const char p2[5]{"test"};
B b1({{{"test"}}});
}
The clang 5.0.0 compiler could not decide which to use and fails with:
29 : <source>:29:11: error: call to constructor of 'C' is ambiguous
B b1({{{"test"}}});
^~~~~~~~~~
5 : <source>:5:40: note: candidate constructor [with T = char, N = 1]
template<typename T, size_t N> C(const T* (&&) [N]) {}
^
6 : <source>:6:40: note: candidate constructor [with T = const char *, N = 1]
template<typename T, size_t N> C(const T (&&) [N]) {}
^
7 : <source>:7:39: note: candidate constructor [with T = char]
template<typename T=char> C(const T* (&&)) {}
^
15 : <source>:15:9: note: passing argument to parameter here
A(C ) {}
^
But if we leave only one of the non-initializer-list constructors the code compiles fine.
GCC 7.2 just picks the C(const T* (&&)) {} and compiles. If it's not available it takes C(const T* (&&) [N]).
MSVC just fails with:
29 : <source>(29): error C2664: 'B::B(B &)': cannot convert argument 1 from 'initializer list' to 'A'
(Edited, thanks #dyp)
Here's a partial answer and speculative, explaining how I've gone about interpreting what happens, not being a compilation expert and not much of a C++ Guru.
First I'll go with some intuition and common sense. Obviously the last thing to happen is a B::B(A), since that's the only constructor available for B b1 (apparently it can't be a B::B(B&&) because there's at least one copy constructor defined, so B::B(B&&) is not implicitly defined for us). Also, the first construction of an A or a B to happen can't be an A::A(const char*) because that one's explicit, so there must be some use of A::A(std::string). Also, the innermost quoted text is a const char[5]. So I'll guess the first, innermost, construction is a const char*; and then a string construction: std::string::string(const char *). There's one more curly-bracket construction, and I would guess it's A::A(A&&) (or maybe A::A(A&)?). So, to summarize my intuitive guess, the order of constructions should be:
A const char*
An std::string (which is really std::basic_string<whatever>)
an A
a B
Then I put this on GodBolt, with GCC as the first example. (Alternatively, you could just compile it yourself while keeping the assembly language output, and pass that through c++filt to make it more readable). Here are all the lines specifically mentioning C++ code:
call 4006a0 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)#plt>
call 400858 <A::A(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>
call 400868 <B::B(A)>
call 400680 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()#plt>
call 400690 <std::allocator<char>::~allocator()#plt>
call 400690 <std::allocator<char>::~allocator()#plt>
So it seems the order of proper actionable constructions we see is:
(not seeing 1.)
2. std::basic_string::basic_string(const char* /* ignoring the allocator */)
3. A::A(std::string)
4. B::B(A)
With clang 5.0.0, results are similar IIANM, and as for MSVC - who knows? Maybe it's a bug? They have been known to be a bit dodgy sometimes on properly supporting the language standard all the way. Sorry, like I said - partial answer.

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.