问题
Consider the following code snippet:
#include <iostream>
struct A {
A() {}
A(const A&) {}
};
struct B {
B(const A&) {}
};
void f(const A&) { std::cout << "A" << std::endl; }
void f(const B&) { std::cout << "B" << std::endl; }
int main() {
A a;
f( {a} ); // A
f( {{a}} ); // ambiguous
f( {{{a}}} ); // B
f({{{{a}}}}); // no matching function
}
Why does each call fabricate the corresponding output? How does the number of braces affect uniform initialization? And how does brace elision affect all this?
回答1:
Overload resolution is fun like this.
{a}
has exact match rank for initializing (a temporary for) theconst A&
parameter, which outcompetes the user-defined conversionB(const A&)
as a realization of{a}
. This rule was added in C++14 to resolve ambiguities in list-initialization (along with adjustments for aggregates).Note that the notional temporary is never created: after overload resolution picks
f(const A&)
, the reference is simply initialized to refer to a, and this interpretation can apply even for non-copyable types.- It would be permissible to initialize a
const A&
parameter (as above) to the constructor for eitherA
orB
, so the call is ambiguous. - Calling a copy constructor (here,
A(const A&)
) repeatedly is prohibited as multiple user-defined conversions—rather than allowing one such conversion per level of overload resolution. So the outermost braces must initialize aB
from theA
initialized from{{a}}
as (permitted) in the second case. (The middle layer of braces could initialize aB
, but copying it with the outer layer would be prohibited and there’s nothing else to try to initialize.) - Every interpretation involves such a disallowed extra conversion.
No brace elision is involved—we don’t know the outermost target type to allow it.
来源:https://stackoverflow.com/questions/54504463/how-does-the-number-of-braces-affect-uniform-initialization