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.
Related
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.
Using Clang 3.7 on windows platform
See following code:
class A1
{
public:
A1(char* name){}
virtual ~A1() {}
private:
A1(const A1&) {}
};
class B1 : public A1
{
public:
B1(): A1(""){}
};
I get the following error:
MyFile(31): 8: error: base class 'A1' has private copy constructor
B1(): A1(""){}
^
MyFile(25): 2: note: declared private here
A1(const A1&) {}
^
Making A1 copy constructor public, eliminates the error!
What happened here?
Note: that by changing (as I should)
A1(const char* name)
I get no errors and all compile as expected
I imagine that this is just an artefact of how the diagnostics are generated.
First, lookup attempts to find a constructor to match your "call" (it's not a call but whatever)
The ctor taking char* doesn't match, as you know
The only other candidate is private
The compiler tries to see whether it can instantiate a temporary A1 from your "" argument to make that work
In doing so, again all it can find is private constructors
The compiler decides to complain about that before doing anything else
One might argue that this is a quality of implementation issue.
Amusingly, GCC 6.1.0 (even in pedantic C++14 mode) compiles your code as originally written, spitting out only a warning for the broken literal conversion.
You cannot call the constructor A1(char* name) using a string literal, becase a string literal is not convertible to char* (such deprecated conversion did exist prior to c++11). Or rather, a program that does call the constructor is ill-formed, and the implementation is allowed to refuse to compile.
As such, the overload resolution looks for other alternatives. The only other potential alternative that has the same number of arguments, is the copy constructor.
For whatever reason, clang appears to prefer the implicit conversion from string literal, to A1, thereby creating a temporary, that could be used for copy-initialization, over using the direct construction from the literal. This behaviour leads to the confusing compilation error.
Both alternatives are ill-formed, and clang appropriately warns about it: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings]. The program does compile, if you set the standard mode to older than c++11 (in which case the program would be well-formed, even though it does use a deprecated conversion). Interestingly, if we disallow the conversion, then the program compiles even in the current standard mode:
class A1
{
public:
explicit A1(char* name){} // note the explicit
virtual ~A1() {}
private:
A1(const A1&) {}
};
G++ behaves differently and your program compiles fine (with the appropriate warning of course). Both compilers appear to comply to the standard in this regard.
Moral of the story: Always read the warnings as well. In this case, the warning was perfectly clear, and easy to solve, while the same bug indirectly caused an error that was not helpful in the solving of the bug.
I have the following trivial struct
struct crazy
{
const int i = 10;
};
which is clearly not copyable. Indeed if I have two objects of that type, say
object1 and object2, and try a statement like
object1 = object2;
both compilers I deal with, namely clang 3.4.2 and gcc 4.8.3 (well, they probably are outdated...), complain and reject the code with reasonable and
similar diagnostics.
But why, in that case, gcc detects an error also in the initialization of the
const member i ? Such error is detected no matter I change "const" with "constexpr".
The errors I get are:
test.cpp: In function ‘int main()’:
test.cpp:10:13: error: use of deleted function ‘crazy& crazy::operator=(const crazy&)’
object1 = object2;
^
test.cpp:3:8: note: ‘crazy& crazy::operator=(const crazy&)’ is implicitly deleted because the default definition would be ill-formed:
struct crazy
^
test.cpp:3:8: error: non-static const member ‘const int crazy::i’, can’t use default assignment operator
The first error is because the assignment operator is deleted, as you say. The second is part of the note explaining why it was deleted: the error that would be generated by the default definition if it weren't deleted. Neither have anything to do with the initialisation of the const member.
If you're asking why the compiler thinks you might want both: because both are helpful to diagnose the problem. Maybe you shouldn't be copying the object, in which case the first tells that you can't. Maybe you want to, in which case the second tells you why you can't.
In a talk about initialization lists, I understood Stroustrup basically saying that the new construction syntax with curly braces is supposed to be a general replacement for all the previous construction syntaxes:
X x1(); // most vexing parse ... doesn't work as intended
X x2(x1);
X x3 = x1;
X x4 = X();
Instead, the new syntax is supposed to be used uniformly as a possible replacement that you can use in every situation ... again, that's the core message that I took from his talk. Maybe I misunderstood him.
So, the question is, how generic is this syntax? Is it possible to never use old-style construction in new C++11 code or are there situations where you have to revert?
This question was triggered/motivated when I encountered the following error, which I believe is an error in the compiler (but I'd be happy to be corrected).
struct X {};
int main() {
X x;
X& y{x}; // works with (x)
X& z{y}; // works with (y)
}
Which doesn't compile on g++ 4.7.1 nor does it on ideone's 4.5.1.
prog.cpp: In function 'int main()':
prog.cpp:5:9: error: invalid initialization of non-const reference of type 'X&' from an rvalue of type '<brace-enclosed initializer list>'
prog.cpp:6:9: error: invalid initialization of non-const reference of type 'X&' from an rvalue of type '<brace-enclosed initializer list>'
Note that it works when I replace X with int.
Brace initialization works everywhere an initializer is used. There are situations where you have to use parens in order to access a constructor that a brace initializer cannot access, but they are rare.
std::vector<int> v(1,1);
std::vector<int> v{1,1};
vector<int> happens to have a specialized constructor that takes two ints and is therefore ambiguous with trying to construct a vector two ints long. The ambiguous constructor exists only for backwards compatibility. New classes should not be defined with any constructors that would conflict with an initializer_list.
The ambiguity is resolved by the fact that brace initialization syntax prefers initializer_list constructors over other constructors that would otherwise match. If you want to resolve the ambiguity in favor of using the non-initializer_list constructor then you can't use brace initialization.
Bjarne Stroustrup writes
The uniform use of {} initialization only became possible in C++11, so older C++ code uses () and = initialization. Consequently, the () and = may be more familiar to you. However, I don't know any logical reason to prefer the () notation except in the rare case where you need to distinguish between initialization with a list of elements and a list of constructor arguments.
— The C++ Programming Language, Fourth Edition §17.3.2.1
Your example code is perfectly legal and should work as expected. The error you're getting is simply a bug in GCC. Clang and VC++ 2012 both accept the code.
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.