there are some codes here
#include <iostream>
struct A {
A(int) {}
};
struct B {
B(A) {
std::cout<<"0"<<std::endl;
}
B(B const&) {
std::cout << "1" << std::endl;
}
B(B&&) {
std::cout << "2" << std::endl;
}
};
int main() {
B b0{{0}}; // this is ok #1
B b( {0} ); //this is error #2
}
g++ report:
main.cpp: In function ‘int main()’:
main.cpp:17:11: error: call of overloaded ‘B(<brace-enclosed initializer list>)’ is ambiguous
B b({ 0 });
^
main.cpp:12:2: note: candidate: B::B(B&&)
B(B&&) {
^
main.cpp:9:2: note: candidate: B::B(const B&)
B(B const&) {
^
main.cpp:6:2: note: candidate: B::B(A)
B(A) {
clang report:
main.cpp:17:4: error: call to constructor of 'B' is ambiguous
B b({ 0 });
^ ~~~~~
main.cpp:6:2: note: candidate constructor
B(A) {
^
main.cpp:12:2: note: candidate constructor
B(B&&) {
^
main.cpp:9:2: note: candidate constructor
B(B const&) {
^
1 error generated.
{0} will convert to temporary object A and the contructor B(A) will be selected, #1 and #2 are all the "direct-constructor" form ,why #1 is ok ,#2 has three candidate constructor and is ambiguous?
The process of selecting the most appropriate overloaded function or operator is called overload resolution. Suppose that f is an overloaded function name. When you call the overloaded function f() , the compiler creates a set of candidate functions.
C++ lets you specify more than one function of the same name in the same scope. These functions are called overloaded functions, or overloads. Overloaded functions enable you to supply different semantics for a function, depending on the types and number of its arguments.
In C++11 and above, we can use the initializer lists '{...}' to initialize a list. This won't work in C++98 as standard permits list to be initialized by the constructor, not by '{...}' .
An overloaded function is really just a set of different functions that happen to have the same name. The determination of which function to use for a particular call is resolved at compile time. In Java, function overloading is also known as compile-time polymorphism and static polymorphism.
Because for #1, the copy and move constructors are disallowed by [over.best.ics]/4 (emphasized mine):
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. [ Note: These rules prevent more than one user-defined conversion from being applied during overload resolution, thereby avoiding infinite recursion. — end note ]
So it is the language rule that distinguishes ({...})
and {{...}}
. Note the ({...})
case falls into [over.match.ctor] but the argument is NOT the temporary in the second step of a class copy-initialization, so the first bullet does not apply.
You can further read Issue 2076 to see it is intended to disallow the copy and move constructors for the inner brace in the {{...}}
case:
The resolution of issue 1467 made some plausible constructs ill-formed. For example,
struct A { A(int); }; struct B { B(A); }; B b{{0}};
This is now ambiguous, because the text disallowing user-defined conversions for B's copy and move constructors was removed from 16.3.3.1 [over.best.ics] paragraph 4...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With