Strange case of C++11 overload resolution - c++

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.

Related

std::packaged_task should have deleted copy c'tor with const parameter

Link https://cplusplus.github.io/LWG/issue2067 provides the following discussion:
Class template packaged_task is a move-only type with the following form of the deleted copy operations:
packaged_task(packaged_task&) = delete;
packaged_task& operator=(packaged_task&) = delete;
Note that the argument types are non-const. This does not look like a typo to me, this form seems to exist from the very first proposing paper on N2276. Using either of form of the copy-constructor did not make much difference before the introduction of defaulted special member functions, but it makes now an observable difference. This was brought to my attention by a question on a German C++ newsgroup where the question was raised why the following code does not compile on a recent gcc:
#include <utility>
#include <future>
#include <iostream>
#include <thread>
int main() {
std::packaged_task<void()> someTask([]{ std::cout << std::this_thread::get_id() << std::endl; });
std::thread someThread(std::move(someTask)); // Error here
// Remainder omitted
}
It turned out that the error was produced by the instantiation of some return type of std::bind which used a defaulted copy-constructor, which leads to a const declaration conflict with [class.copy] p8.
Some aspects of this problem are possibly core-language related, but I consider it more than a service to programmers, if the library would declare the usual form of the copy operations (i.e. those with const first parameter type) as deleted for packaged_task to prevent such problems.
Could anybody explain the meaning of the marked statement? I don't undestand how the missing const qualifer affects the compilation process, and how this behavior is explained in standard.
What is the point of adding const to the parameter of the deleted copy constructor?
Here is a toy example:
struct problem {
problem()=default;
problem(problem&&)=default;
problem(problem&)=delete;
};
template<class T>
struct bob {
T t;
bob()=default;
bob(bob&&)=default;
bob(bob const&)=default;
};
int main() {
problem p;
problem p2 = std::move(p);
bob<problem> b;
bob<problem> b2 = std::move(b);
}
bob<problem> fails to compile because the bob(bob const&)=default errors out when it interacts with problem(problem&)=delete.
Arguably the standard "should" error-out cleanly when it determines that it cannot implement bob(bob const&), and treat the =default as =delete (like it would if we had problem(problem const&)=delete), but the standard wording isn't going to be flawless in this corner case of a corner case. And this corner of a corner case is going to be strange and quirky enough that I'm not certain a general rule that makes it translate =default to =delete would be right!
The fix if we problem(problem const&)=delete (well, to packaged_task) is going to be so much cleaner than anything we do to =default ctor rules.
Now standard delving:
First, it is obvious that the implicitly declared copy constructor of bob<problem> above is going to have signature bob(bob&) in [class.ctor]. I won't even quote the standard for that, because lazy.
We go and explicitly default bob(bob const&) copy ctor, which differs in signature from the one that would be implicitly declared.
There are rules about explicitly defaulting functions and their conflict with the signatures is in 11.4.2.
In Explicitly-defaulted functions[dcl.fct.def.default] 11.4.2/2
2 The typeT1of an explicitly defaulted function F is allowed to differ from the type T2 it would have had if it were implicitly declared, as follows:
—(2.1) T1 and T2 may have differing ref-qualifiers; and
—(2.2) if T2 has a parameter of type const C&, the corresponding parameter of T1 may be of type C&.
If T1 differs from T2 in any other way, then:
—(2.3) if F is an assignment operator, and the return type of T1 differs from the return type of T2 or T1’s parameter type is not a reference, the program is ill-formed;
—(2.4) otherwise, if F is explicitly defaulted on its first declaration, it is defined as deleted;
—(2.5) otherwise, the program is ill-formed.
The defaulted one is T1, which contains const& not &, so (2.2) doesn't apply.
My reading actually has it getting caught on (2.4); the type of bob(bob const&) differs from the implicitly declared bob(bob&) in an impermissible way; but first declaration is defaulted, so it should be deleted.
I'm looking at the n4713 draft version; maybe an older version didn't have that clause.

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.

C++11 constructor overload resolution and initialiser_lists: clang++ and g++ disagree

I have a small piece of C++11 code which g++ (4.7 or 4.8) refuses to compile claiming that the call to constructor for B2 b2a(x, {P(y)}) is ambiguous. Clang++ is happy with that code, but refuses to compile B2 b2b(x, {{P(y)}}) which g++ is perfectly happy to compile!
Both compilers are perfectly happy with the B1 constructor with either {...} or {{...}} as an argument. Can any C++ language lawyer explain which compiler is correct (if either) and what is going on? Code below:
#include <initializer_list>
using namespace std;
class Y {};
class X;
template<class T> class P {
public:
P(T);
};
template<class T> class A {
public:
A(initializer_list<T>);
};
class B1 {
public:
B1(const X&, const Y &);
B1(const X&, const A<Y> &);
};
class B2 {
public:
B2(const X &, const P<Y> &);
B2(const X &, const A<P<Y>> &);
};
int f(const X &x, const Y y) {
B1 b1a(x, {y});
B1 b1b(x, {{y}});
B2 b2a(x, {P<Y>(y)});
B2 b2b(x, {{P<Y>(y)}});
return 0;
}
and the compiler errors, clang:
$ clang++ -stdlib=libc++ -std=c++11 test-initialiser-list-4.cc -o test.o -c
test-initialiser-list-4.cc:32:6: error: call to constructor of 'B2' is ambiguous
B2 b2(x, {{P<Y>(y)}});
^ ~~~~~~~~~~~~~~
test-initialiser-list-4.cc:26:5: note: candidate constructor
B2(const X &, const P<Y> &);
^
test-initialiser-list-4.cc:27:5: note: candidate constructor
B2(const X &, const A<P<Y>> &);
^
g++:
test-initialiser-list-4.cc: In function 'int f(const X&, Y)':
test-initialiser-list-4.cc:32:21: error: call of overloaded 'B2(const X&, <brace-enclosed initializer list>)' is ambiguous
B2 b2(x, {P<Y>(y)});
^
test-initialiser-list-4.cc:32:21: note: candidates are:
test-initialiser-list-4.cc:27:5: note: B2::B2(const X&, const A<P<Y> >&)
B2(const X &, const A<P<Y>> &);
^
test-initialiser-list-4.cc:26:5: note: B2::B2(const X&, const P<Y>&)
B2(const X &, const P<Y> &);
^
This smells like an interaction between uniform initialisation, initialiser list syntax and function overloading with templated arguments (which I know g++ is fairly stringent about), but I'm not enough of a standards lawyer to be able to unpack what should be the correct behaviour here!
First code, then what I think should happen. (In what follows, I will ignore the first parameter, since we are interested only into the second parameter. The first one is always an exact match in your example). Please note that the rules are currently in flux in the spec, so I wouldn't say that one or the other compiler has a bug.
B1 b1a(x, {y});
This code cannot call the const Y& constructor in C++11, because Y is an aggregate and Y has no data member of type Y (of course) or something else initializable by it (this is something ugly, and is worked on to be fixed - the C++14 CD doesn't have wording for this yet, so I am not sure whether final C++14 will contain this fix).
The constructor with the const A<Y>& parameter can be called - {y} will be taken as the argument to the constructor of A<Y>, and will initialize that constructor's std::initializer_list<Y>.
Hence - second constructor called successfully.
B1 b1b(x, {{y}});
Here, the basically same argument counts counts for the constructor with the const Y& parameter.
For the constructor with parameter type const A<Y>&, it is a bit more complicated. The rule for conversion cost in overload resolution computing the cost of initializing an std::initializer_list<T> requires every element of the braced list to be convertible to T. However we before said that {y} cannot be converted to Y (as it is an aggregate). It is now important to know whether std::initializer_list<T> is an aggregate or not. Frankly, I have no idea whether or not it must be considered to be an aggregate according to the Standard library clauses.
If we take it to be a non-aggregate, then we would be considering the copy constructor of std::initializer_list<Y>, which however again would trigger the exact same sequence of tests (leading to "infinite recursion" in overload resolution checking). Since this is rather weird and non-implementable, I don't think any implementation takes this path.
If we take std::initializer_list to be an aggregate, we will be saying "nope, no conversion found" (see the above aggregates-issue). In that case since we cannot call the initializer constructor with the single initializer list as a whole, {{y}} will be split up into multiple arguments, and the constructor(s) of A<Y> will be taking each of those separately. Hence, in this case, we would end up with {y} initializing a std::initializer_list<Y> as the single parameter - which is perfectly fine and work like a charm.
So under the assumption that std::initializer_list<T> is an aggregate, this is fine and call the second constructor successfully.
B2 b2a(x, {P<Y>(y)});
In this case and the next case, we don't have the aggregate issue like above with Y anymore, since P<Y> has a user-provided constructor.
For the P<Y> parameter constructor, that parameter will be initialized by {P<Y> object}. As P<Y> has no initializer lists, the list will be split up into individual arguments and call the move-constructor of P<Y> with an rvalue object of P<Y>.
For the A<P<Y>> parameter constructor, it is the same as the above case with A<Y> initialized by {y}: Since std::initializer_list<P<Y>> can be initialized by {P<Y> object}, the argument list is not split, and hence the braces are used to initializer that constructor'S std::initializer_list<T>.
Now, both constructors work fine. They are acting like overloaded functions here, and their second parameter in both cases requires a user defined conversion. User defined conversion sequences can only be compared if in both cases the same conversion function or constructor is used - not the case here. Hence, this is ambiguous in C++11 (and in the C++14 CD).
Note that here we have a subtle point to explore
struct X { operator int(); X(){/*nonaggregate*/} };
void f(X);
void f(int);
int main() {
X x;
f({x}); // ambiguity!
f(x); // OK, calls first f
}
This counter intuitive result will probably be fixed in the same run with fixing the aggregate-initialization weirdness mentioned above (both will call the first f). This is implemented by saying that {x}->X becomes an identity conversion (as is X->x). Currently, it is a user-defined conversion.
So, ambiguity here.
B2 b2b(x, {{P<Y>(y)}});
For the constructor with parameter const P<Y>&, we again split the arguments and get {P<Y> object} argument passed to the constructor(s) of P<Y>. Remember that P<Y> has a copy constructor. But the complication here is that we are not allowed to use it (see 13.3.3.1p4), because it would require a user defined conversion. The only constructor left is the one taking Y, but an Y cannot be initialized by {P<Y> object}.
For the constructor with parameter A<P<Y>>, the {{P<Y> object}} can initialize a std::initializer_list<P<Y>>, because {P<Y> object} is convertible to P<Y> (other than with Y above - dang, aggregates).
So, second constructor called successfully.
Summary for all 4
second constructor called successfully
under the assumption that std::initializer_list<T> is an aggregate, this is fine and call the second constructor successfully
ambiguity here
second constructor called successfully

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.

Templates don't always guess initializer list types

#include <initializer_list>
#include <utility>
void foo(std::initializer_list<std::pair<int,int>>) {}
template <class T> void bar(T) {}
int main() {
foo({{0,1}}); //This works
foo({{0,1},{1,2}}); //This works
bar({{0,1}}); //This warns
bar({{0,1},{1,2}}); //This fails
bar(std::initializer_list<std::pair<int,int>>({{0,1},{1,2}})); //This works
}
This doesn't compile in gcc 4.5.3, it gives a warning for the marked line saying deducing ‘T’ as ‘std::initializer_list<std::initializer_list<int> >’ and an error for the marked line saying no matching function for call to ‘bar(<brace-enclosed initializer list>)’. Why can gcc deduce the type of the first call to bar but not the second, and is there a way to fix this other than long and ugly casting?
GCC according to C++11 cannot deduce the type for either first two calls to bar. It warns because it implements an extension to C++11.
The Standard says that when a function argument in a call to a function template is a { ... } and the parameter is not initializer_list<X> (optionally a reference parameter), that then the parameter's type cannot be deduced by the {...}. If the parameter is such a initializer_list<X>, then the elements of the initializer list are deduced independently by comparing against X, and each of the deductions of the elements have to match.
template<typename T>
void f(initializer_list<T>);
int main() {
f({1, 2}); // OK
f({1, {2}}); // OK
f({{1}, {2}}); // NOT OK
f({1, 2.0}); // NOT OK
}
In this example, the first is OK, and the second is OK too because the first element yields type int, and the second element compares {2} against T - this deduction cannot yield a constradiction since it doesn't deduce anything, hence eventually the second call takes T as int. The third cannot deduce T by any element, hence is NOT OK. The last call yields contradicting deductions for two elements.
One way to make this work is to use such a type as parameter type
template <class T> void bar(std::initializer_list<std::initializer_list<T>> x) {
// ...
}
I should note that doing std::initializer_list<U>({...}) is dangerous - better remove those (...) around the braces. In your case it happens to work by accident, but consider
std::initializer_list<int> v({1, 2, 3});
// oops, now 'v' contains dangling pointers - the backing data array is dead!
The reason is that ({1, 2, 3}) calls the copy/move constructor of initializer_list<int> passing it a temporary initializer_list<int> associated with the {1, 2, 3}. That temporary object will then be destroyed and die when the initialization is finished. When that temporary object that is associated with the list dies, the backing-up array holding the data will be destroyed too (if the move is elided, it will live as long as "v"; that's bad, since it would not even behave bad guaranteedly!). By omitting the parens, v is directly associated with the list, and the backing array data is destroyed only when v is destroyed.
List initialization relies on knowing what type is being initialized. {1} could mean many things. When applied to an int, it fills it with a 1. When applied to a std::vector<int>, it means to create a one-element vector, with 1 in the first element. And so on.
When you call a template function who's type is completely unconstrained, then there is no type information for list initialization to work with. And without type information, list initialization cannot work.
For example:
bar({{0,1}});
You expect this to be of the type std::initializer_list<std::pair<int,int>>. But how could the compiler know that? bar's first parameter is an unconstrained template; it can literally be any type. How could the compiler possibly guess that you meant this specific type?
Quite simply, it can't. Compilers are good, but they're not clairvoyant. List initialization can only work in the presence of type information, and unconstrained templates remove all of that.
By all rights, that line should have failed to compile, according to C++11. It cannot deduce what type you intended the {...} to be, so it should have failed. That looks like a GCC bug or something.