How does the number of braces affect uniform initialization?

  • A+

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?


Overload resolution is fun like this.

  1. {a} has exact match rank for initializing (a temporary for) the const A& parameter, which outcompetes the user-defined conversion B(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).
  2. It would be permissible to initialize a const A& parameter to the constructor for either A or B with that exact match, so the call is ambiguous.
  3. 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 exact-match-copy a B constructed by the middle set of braces.
  4. Every interpretation involves such a disallowed extra conversion.

No brace elision is involved—we don’t know the outermost target type to allow it.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: