0

To the best of my understanding, uniform initialization is the preferred syntax for initializing objects. Herb Sutter writes

For one thing, it’s called “uniform initialization” because it’s, well, uniform—the same for all types, including aggregate structs and arrays and std:: containers...

and the accepted answer to this question states

Prefer {} initialization over alternatives unless you have a strong reason not to.

However, consider this code:

#define BRACES                                                                                                                                                                                           

template<typename V>
class foo {   
public:
    template<typename W>
    explicit foo(const W &w) : 
#ifdef BRACES
        m_v{w}
#else // #ifdef BRACES
        m_v(w)
#endif // #ifdef BRACES
    {}  

private:
    V m_v;
};  

struct bar{};  

int main() 
{   
    bar b;

    foo<bar>{b};
#ifdef BRACES
    bar c{b};
#else // #ifdef BRACES
    bar c(b);
#endif // #ifdef BRACES                                                                                                                                                                                  
}   

If #define BRACES is uncommented, this code fails to build (g++ 4.8.5) with error: too many initializers for ‘bar’ on the line

    m_v{w}

in the constructor of foo. This makes sense, as the more direct invocation

 bar c{b};

in main fails similarly, and they are essentially the same (although the template code does not know that).

Conversely, commenting #define BRACES causes everything to build. Is this an indication to avoid this form of initialization in this type of template code?

Edit

@melak47 pointed out that this problem does not appear in g++5.1, and gave convincing proof. It apparently disappeared somewhere between 4.8.5 and 5.1.

Community
  • 1
  • 1
Ami Tavory
  • 74,578
  • 11
  • 141
  • 185

2 Answers2

2

List-initialization didn't quite work when you attempt to initialize an aggregate from something of the same type.

This is CWG 1467, whose resolution (among other things) introduced another bullet to the giant list in [dcl.init.list]/3 to make this work:

List-initialization of an object or reference of type T is defined as follows:

  • If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).
T.C.
  • 133,968
  • 17
  • 288
  • 421
-1

When the overloaded constructor is resolved, braced initialization will match a constructor using std::initializer_list parameters, before considering other overloaded constructors. So

bar c{b};

will match to a constructor taking std::initializer_list, rather than the generated copy constructor.

This is discussed in Item 7: Distinguish between () and {} when creating objects of Scott Meyers - Effective Modern C++.

Also, Wikipedia - C++11 Uniform initialization

Andrew Duncan
  • 174
  • 1
  • 3