GotW #1 Solution: Variable Initialization – or Is It?

This first problem highlights the importance of understanding what you write. Here we have a few simple lines of code—most of which mean something different from all the others, even though the syntax varies only slightly.

 

Problem

JG Question

1. What is the difference, if any, among the following?

widget w;                   // (a)

widget w(); // (b)
widget w{}; // (c)

widget w(x); // (d)
widget w{x}; // (e)

widget w = x; // (f)
widget w = {x}; // (g)

auto w = x; // (h)
auto w = widget{x}; // (i)

Guru Questions

2. What do each of the following lines do?

vector<int> v1( 10, 20 );   // (a)

vector<int> v2{ 10, 20 }; // (b)

3. Besides the cases above, what other benefits are there to using { } to initialize objects?

4. When should you use ( ) vs. { } syntax to initialize objects? Why?

 

Solution

This puzzle demonstrates several things:

  • The difference between default initialization, direct initialization, copy initialization, and list initialization.
  • The difference between using ( ) and using { } for initialization.
  • A red herring that isn’t initialization at all, and which modern C++ entirely avoids.

But, most important of all: If you stick to two simple Guidelines, which we’ll cover in #4, you can ignore most of these cases and the rules are pretty simple and deliver efficient performance by default.

 

1. What is the difference, if any, among the following?

Let’s consider the cases one by one.

(a) is default initialization.

widget w;                   // (a)

This code declares a variable named w, of type widget. For most types, it is initialized using the default constructor widget::widget().

Note that w is not initialized and contains garbage values if widget happens to be a built-in type like int, or a simple “int-like” class type with what’s called a “trivial” default constructor—a type that relies on the compiler-generated default constructor, has no virtual functions or virtual base classes or data member initializers, and all its bases and members satisfy the same restrictions.

(b) is a “vexing” red herring, now mostly a historical curiosity.

widget w();                 // (b)

This is a pre-modern C++ pitfall: At first glance, it may look like just another variable declaration calling a default constructor widget::widget(); in reality, thanks to a grammar ambiguity, it’s a function declaration for a function named w that takes no parameters and returns a widget object by value. (If you can’t see this at first, consider that the above code is no different from writing something like int f(); which is clearly a function declaration.)

Lest you think “aw, but those ( ) parentheses are redundant, it’s the programmer’s own fault for not just writing widget w; there!”, note that the same problem arises in those occasional cases where you think you’re initializing a variable with temporary objects:

// same problem (gadget and doodad are types)
//
widget w( gadget(), doodad() ); // pitfall: not a variable declaration

Scott Meyers long ago named this “C++’s most vexing parse,” because the standard resolves the parsing ambiguity by saying: “if it can be a function declaration, it is.”

The good news is that this is now mostly a historical curiosity, not something you should encounter in new code, because C++11 removed this pitfall. Note that C++11 does not change the meaning of the code—C++11 has great backward compatibility with C++98, including that this ambiguity still has the same meaning it always had. Rather, C++11 solves this by providing a syntax that supersedes case (b) in nearly all cases, so that we don’t need to ever fall into this pit anymore:

(c) is non-vexing and clear.

widget w{};                 // (c)

Here we have the first reason to prefer { } to ( ): For any class type widget, line (c) does the “best parts” of (a) and (b)—it always initializes the variable, and is never ambiguous with a function declaration. No vex, no fuss, no muss.

“Aha, but wait, it’s not that simple!” someone might object. “What if widget has a constructor that takes a std::initializer_list? Those are greedy (preferred), so if widget has one of those wouldn’t this call that?”

The answer is no, this really is just as simple as it looks, because the standard is explicit that an empty { } list means to call the default constructor if available. However, it’s good to be aware of initializer_lists, so let’s talk about those next.

(d) and (e) are direct initialization.

Now let’s consider cases where we actually initialize w from some existing variable:

widget w(x);                // (d)
widget w{x}; // (e)

Assuming x is not the name of a type, these are both direct initialization. That’s because the variable w is initialized “directly” from the value of x by calling widget::widget(x). If x is also of type widget, this invokes the copy constructor. Otherwise, it invokes a converting constructor.

However, note that the syntax {x} creates an initializer_list. If widget has a constructor that takes an initializer_list, that constructor is preferred; otherwise, if widget has a constructor that takes whatever type x is (possibly with conversions), that constructor is used.

There are two major differences that make (e) superior to (d): First, like (c), syntax (e) is unambiguous and avoids the vexing parse. If x is a type name, then (d) is a function declaration even if there is also a variable named x in scope (see above), whereas (e) is never a function declaration.

Second, syntax (e) is safer because it does not allow narrowing (a.k.a. “lossy”) conversions that are otherwise allowed for some built-in types. Consider:

int i1( 12.345 );           // ok: toss .345, we didn't like it anyway
int i2{ 12.345 }; // error: would be lossy implicit narrowing

(f) and (g) are copy initialization and copy list initialization.

This brings us to our final two non-auto cases:

widget w = x;               // (f)

This is called “copy initialization.” Conceptually, the variable w is initialized using widget‘s move or copy constructor, possibly after calling another function to convert the argument implicitly (explicit conversions won’t be invoked here).

Common Mistake: This is always initialization; it is never assignment, and so it never calls T::operator=(). Yes, I know there’s an “=” character in there, but don’t let that throw you — that’s just a syntax holdover from C, not an assignment operation.

Here are the semantics:

  • If x is of type widget, line (f) means the same as (d) widget w(x); except that explicit constructors cannot be used. It’s guaranteed that only a single constructor is called.
  • If x is of some other type, conceptually the compiler first implicitly converts x to a temporary widget object, then move-constructs w from that temporary rvalue, using copy construction as “the slow way to move” as a backup if no better move constructor is available. Assuming that an implicit conversion is available, (f) means the same as widget w( widget(x) );.

Note that I said “conceptually” a few times above. That’s because practically compilers are allowed to, and routinely do, optimize away the temporary and, if an implicit conversion is available, convert (f) to (d), thus optimizing away the extra move operation. However, even when the compiler does this, the widget copy constructor must still be accessible, even if is not called—the copy constructor’s side effects may or may not happen, that’s all.

Now note the related syntax that adds “=“:

widget w = {x};             // (g)

This is called “copy list initialization.” It means the same as widget w{x}; except that explicit constructors cannot be used. It’s guaranteed that only a single constructor is called.

(h) and (i) are also copy initialization, but simpler.

auto w = x;                 // (h)
auto w = widget{x}; // (i)

The semantics are just like (f) and (g), except simpler to teach, learn, and use because using auto guarantees the right-hand expression’s type will be deduced exactly. Note that the (i) syntax works fine for both implicit and explicit conversions.

Line (h) means the same as (d), type_of_x w(x);. Only a single copy constructor is called. This is guaranteed to stay true as the program evolves: Because line (h) does not commit to an explicit type, it is guaranteed to be both maximally efficient because there can be no conversion involved, and maximally robust under maintenance as the type of w “auto”-matically tracks the type of x which may change as the program is maintained.

Line (i) is the most consistent spelling when you do want to commit to a specific type and explicitly request a conversion if needed, and once again the { } syntax happily avoids lossy narrowing conversions. In practice on most compilers, only a single constructor is called—similarly to what we saw with (f) and (g), conceptually there are two constructor calls, a converting or copy constructor to create a temporary widget{x} followed by a move to move it to w, but compilers routinely elide the latter.

In general, I recommend that you try out these two forms, and increasingly prefer using them as you grow comfortable with them. I’m at the point where I’m now inclined to write virtually all of my local variable declarations this way. (I know some of you will be skeptical about this broad claim—more on “the auto question” in another GotW.)

 

2. What do each of the following lines do?

In the Question 2 code, we’re creating a vector<int> and passing the arguments 10 and 20 to its constructor—in the first case as ( 10, 20 ) and in the second case as { 10, 20 }.

Both will call a constructor, but which one(s)? Well, vector<int> has several constructors that can take two parameters, but only two could be correctly called with the parameters 10 and 20. Ignoring defaulted optional allocator parameters for simplicity, the two constructors are:

vector( size_t n, const int& value );    // A: n copies of value

vector( initializer_list<int> values ); // B: copy of values

There are two simple C++ rules that tell us which one will be called for the code in question:

  • The syntax { /*…*/ } used in an expression context gives you an initializer_list.
  • Constructors that take an initializer_list are preferred over other constructors, and so can hide other constructors that might otherwise be viable.

Armed with those two tidbits, the answer is simple:

vector<int> v1( 10, 20 );    // (a) calls A: 10 copies of the value 20
assert( v1.size() == 10 );

vector<int> v2{ 10, 20 }; // (b) calls B: the values 10 and 20
assert( v2.size() == 2 );

 

3. Besides the cases above, what other benefits are there to using { } to initialize objects?

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 without the “vexing parse” annoyance:

struct mystruct { int x, y; };

// C++98
rectangle w( origin(), extents() ); // oops, vexing parse
complex<double> c( 2.71828, 3.14159 );
mystruct m = { 1, 2 };
int a[] = { 1, 2, 3, 4 };
vector<int> v; // urk, need more code
for( int i = 1; i <= 4; ++i ) v.push_back(i); // to initialize this

// C++11 (note: "=" is mostly optional)
rectangle w = { origin(), extents() };
complex<double> c = { 2.71828, 3.14159 };
mystruct m = { 1, 2 };
int a[] = { 1, 2, 3, 4 };
vector<int> v = { 1, 2, 3, 4 };

And note that this isn’t just an aesthetic issue. Consider writing generic code that should be able to initialize any type… and while we’re at it, let’s gratuitously use perfect forwarding as an example:

template<typename T, typename ...Args>
void forwarder( Args&&... args ) {
// ...
T local = { std::forward<Args>(args)... };
// ...
}

forwarder<int> ( 42 ); // ok
forwarder<rectangle> ( origin(), extents() ); // ok
forwarder<complex<double>>( 2.71828, 3.14159 ); // ok
forwarder<mystruct> ( 1, 2 ); // ok because of {}
forwarder<int[]> ( 1, 2, 3, 4 ); // ok because of {}
forwarder<vector<int>> ( 1, 2, 3, 4 ); // ok because of {}

The last three lines would not be legal if forwarder used ( ) initialization syntax internally.

The new { } syntax works pretty much everywhere, including to initialize members:

widget::widget( /*...*/ ) : mem1{init1}, mem2{init2, init3} { /*...*/ } 

And, as icing on the take, it’s often just plain convenient to pass function arguments, or return a value, without a type-named temporary:

void draw_rect( rectangle ); 

draw_rect( rectangle(origin, selection) ); // C++98
draw_rect({ origin, selection }); // C++11

rectangle compute_rect() {
// ...
if(cpp98) return rectangle(origin, selection); // C++98
else return {origin, selection}; // C++11
}

 

4. When should you use ( ) vs. { } syntax to initialize objects? Why?

Here’s the simple guideline:

Guideline: Prefer to use initialization with { }, such as vector<int> v = { 1, 2, 3, 4 }; or auto v = vector<int>{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine. …

That covers the vast majority of cases. There is only one main exception:

… In rare cases, such as vector<int> v(10,20); or auto v = vector<int>(10,20);, use initialization with ( ) to explicitly call a constructor that is otherwise hidden by an initializer_list constructor.

However, the reason this should be generally “rare” is because default and copy construction are already special and work fine with { }, and good class design now mostly avoids the resort-to-( ) case for user-defined constructors because of this final design guideline:

Guideline: When you design a class, avoid providing a constructor that ambiguously overloads with an initializer_list constructor, so that users won’t need to use ( ) to reach such a hidden constructor.

 

Acknowledgments

Thanks in particular to the following for their feedback to improve this article: Michal Mocny, Jay Miller, “Alexey,” “praetorian20,” Francisco Lopes, “Neil,” Daryle Walker.

55 thoughts on “GotW #1 Solution: Variable Initialization – or Is It?

  1. I believe I may have uncovered an uncomfortable interaction between the new C++11 uniform initialization syntax, and everybody’s old friend, the preprocessor. When the preprocessor breaks down a macro argument list into individual argument tokens, it has special handling for commas which are embedded in parenthesis (so an entire function call is considered to be a single macro argument), whereas it doesn’t do the same thing for braces. So a new-style initialization expression which takes more than one argument (i.e. has a comma in it) would appear to the preprocessor as multiple arguments to a macro.

    This listing demonstrates the problem (if you can ignore its many, many issues). I tested it MinGW/gcc -std=c++11 and with VS2013 cl.exe:

    #include <vector>
    #include <stdio.h>
    
    
    // Show the first value of a std::vector<int>
    #define SHOW_VALUE(X_) \
        SHOW_VALUE_(__FILE__, __LINE__, X_)
    
    #define SHOW_VALUE_(F_, L_, X_) \
        (::printf( F_ ":%u: " #X_ " = %u\n", L_, ((X_)[0]) ))
    
    
    int main() {
        auto oldv = std::vector<int>(1, 2);
        auto newv = std::vector<int>{1, 2};
    
        SHOW_VALUE(oldv);
        SHOW_VALUE(newv);
        SHOW_VALUE(std::vector<int>(3, 4));
    //    SHOW_VALUE(std::vector<int>{3, 4}); //Oops: SHOW_VALUE() sees 2 arguments
        SHOW_VALUE((std::vector<int>{3, 4})); //Clunky workaround for line above
    
        return 0;
    }
    

    So in the commented out line, SHOW_VALUE() sees arguments of ‘std::vector<int&gt{3’ and ‘4}’.

    “Don’t use macros” aside, this example is pretty contrived, and nobody is likely to need to dump the value of an object literal in this manner. It’s also possible to work around it by wrapping the literal in parentheses, so it’s not world-ending. (It’s also possible to build macro machinery to detect and remove parentheses from macro arguments with the C99 preprocessor, if necessary.)

    (Going off on a tangent, I find myself wandering if the best way to get a ‘fix’ for this might be a proposal for the next C standard for the preprocessor to handle {,} in the same way it currently handles (,) — that would allow you to pass multiple C array literals into a single macro for example, and would seem to be a strict extension to the current behaviour, as only variadic macros can process an array literal currently.)

  2. in a) “Note that w is not initialized and contains garbage values if widget happens to be a built-in type like int”…

    Depends on where w is declared (for example, what if it is global). Not trying to be picky, but if someone does not already know the answer to a, then they probably also do not know the rules of initialized vs uninitialized for those cases.

  3. Hi, I`m trying to understand this:
    widget w = x; // (f)
    You have commented that:
    >> If x is of some other type, …. Assuming that an implicit conversion is available, (f) means the same as widget w( widget(x) );.
    I do not understand why ? I wrote a test using VS 2013 and found that it invokes only one constructor (and it`s this pointed to the x) :

    struct widget {
    widget(int) {
    cout << "widget(int)\n";
    }

    widget(const widget &) {
    cout << "widget(int)\n";
    }
    };

    widget x = 5;

    What am I doing wrong ?

    Thanks

  4. Just want to thank you Herb for your GotW articles. Your explanations by example has really helped me. I have a question that is related to Copy Constructors and c++11. I was making a class recently and I was writing the copy constructor for it. I realized that it was basically a duplicate of the explicit normal constructor that I just wrote. Fortunately c++11 allows constructors to call peer constructors using delegation. So could I just have my copy constructor call my explicit normal constructor? Is this acceptable?

  5. @Anubhav: Yes, the first bullet does apply, but the invoked constructor may be trivial. References to N3690 (current draft): 8.5/12: “If no initializer is specified for an object, the object is default-initialized…”. Which takes us to: 8.5/7: “if T is a (possibly cv-qualified) class type… the default constructor (12.1) for T is called…”. If widget is a class type, which can still be trivial or int-like, this takes us to: 12.1/4 which says that “… the default constructor is trivial” if the list I mentioned in 1(a) is satisfied. So yes, a constructor is called, but it may be trivial and have no effect. When construction is trivial, the language also allows you to access members before the constructor runs, goto past a local variable’s initialization, and such.

  6. In 1 (a), is it totally correct to say ‘Note that w is not initialized…’ for the case of a trivial class? $8.5/6 third bullet does not apply to this case (the first bullet applies) and hence I think that initialization is performed.

  7. In 1 (a) default initialization, is it correct to say ‘Note that w is not initialized…’. when the type involved in a trivial class type? $8.5/6 third bullet does not apply to this case, and hence it appears that ‘..is not initialized’ is not totally correct.

  8. @Seungbum Woo @Herb Sutter
    Just for curious, i check Seungbum’s code under archlinux_64, g++4.8.1

    F(const vector &vv) : v{vv} { ... } // #1
    ...

    main()
    {
    ...
    F f{a}; // #2
    f.test(); // #3
    }


    by initialization v in {} way(#1), the output will be:

    2
    [1] 8742 segmentation fault (core dumped) ./a.out

    gdb a.out turn out that, after execution of #2, the f object in memory look like this:

    (gdb) p f
    $1 = {v = std::vector of length 2, capacity 2 = {0, 0}}

    but still can’t figure out why the segmentation fault.

  9. @Laurent: Consider T{x} — that’s the same as T(x) except that the former doesn’t allow narrowing and does prefer initializer_lists if any, so T(x) and T{x} are both semantically a function-style cast to T, if x is not of type T or derived from T.

  10. Herb,

    I looked a bit into N3651 (variable templates, revision 1) and something shocked me when I read the first example – template definition of the pi constant. The first thing I thought about was “Hey! This must be an old paper, they’re not using {}-initialization.” But then, I thought again, and saw that it was on purpose. And I thought about this a little more, and exclaimed “Hey! For any type T, the single-parametered syntax T(x) now uniformly means something different than it used to, doesn’t it?”

    Since you’re saying that {} should be preferred in nearly all cases, I think C++11 now has two syntaxes for two different concepts:
    – T{x, y, z, ….} uniformly means initialization.
    – T(x) uniformly means “convert x to T”.

    Am I wrong? Any thoughts?

  11. I am using GCC 4.8.1 under Windows7 ( using MinGW-w64 ) I am using an experimental version of MinGW-w64 for its std::thread support. I should try MinGW-w64’s released version.

    I confirmed that VC++ 2012 Nov CTP works fine with v{vv}.

    Thank you for your answer.

  12. @Seungbum Woo: Looks like a compiler bug. What compiler and environment are you using?

    I just tried:

    – GCC 4.8 and VC++ 2012 Nov CTP which both like it;

    – GCC 4.7.2 which thinks F f{a}; is illegal; and

    – Intel ICC 13.0.1 which thinks v{vv} is illegal.

    I believe GCC 4.8 and VC++ 2012 Nov CTP have it right.

  13. #include <iostream>
    #include <vector>
    using namespace std;
    
    class F {
    public:
        F(const vector<int>& vv) :v{vv} { cout << v[0] << endl; }
        void test() { cout << v[0] << endl; }
    private:
        const vector<int>& v;
    };
    
    int main()
    {
        vector<int> a {2,3};
        F f {a};
        f.test();
    }
    

    The output of this is
    2
    garbage

    if I change the F constructor to

         F(const vector<int>& vv) :v(vv) { cout << v[0] << endl; }
    

    The output is correct as expected :
    2
    2

    Why does the first one with {} not work as expected? Thanks!

  14. @Alexey, @Daryle: I was trying to talk about just normal class types and avoid getting into types with trivial default constructors (and terms like “defaulted default constructor” that appear in the standard), but since there have been several comments about that you’ve convinced me that I should. I’ve updated the descriptions of (a) and (c) to talk about that. (I think the actual wording wanted in this context is not that it’s a “trivial type” but rather that it’s a “type with a trivial default constructor” — the two are basically the same thing except “trivial type” additionally means trivially copyable, or bit-copyable, and all that applies here is the trivial default constructibility.)

  15. Found it :)

    12.6.1 Explicit initialization

    At the end of the example it says explicitly:

    ” [ Note: overloading of the assignment operator (13.5.3) has no effect on initialization. — end
    note ]”

  16. Whoa, cases 1a and 1c are not exactly the same. If the type is considered trivial, i.e. can have a code-less definition, then 1a will leave the object with random garbage bits. If such types are used like 1c, then value-initialization happens and the object is set to zero (if scalar) or recursively value-initialized (if composite). The format for 1a is called default-initialization. If the type is a class-type with a code-ful default construction policy (i.e. not trivial), then default-initialization will secretly call value-initialization instead, and that’s the only time where the two declarations are the same.

  17. It should be mentioned at least in one of these comments that

    widget w ();

    Is not a vexing parse. In the grammar it can only be a function declaration. Just to the human reader it might be ambiguous as to what it means (but so might some of the brace initialization scenarios aswell).

  18. Herb in your auto GotW dont forget to mention that auto doesnt mean that type is the same as what function returns, aka
    const std::string& MyClass::blah()
    auto s = mc.blah() // s is a string, not string ref

  19. There is also another form of initialization

    widget w({x});

    Which is valid only for classes, and calls directly the constructors, with “{x}” as argument.

  20. It is remarkable that a difference between f and g is that copy list initialization does not prevent an explicit constructor to be selected. However if one is chosen, the program becomes illformed. This too was done as a safety feature, to prevent a less matching implicit constructor becoming selected.

    You have a vexing error where you are saying that the following is an initialization. It is actually a function declaration

    widget w ( widget (x) );

  21. std::array has no constructor, so you need either double braces or = init syntax(c initialization). atleast thats what i remember.

  22. I think you should also mention that std::array has to be initialized with ‘=’ sign.

  23. Seriously, we need a different font on this blog so that it’s _much_ easier to distinguish between () and {}.

  24. I don’t grok the standard’s need for any ambiguity here.

    Perhaps someone can explain why something along the following lines was not adopted as standard?:

    // Try to match a constructor then try to promote to a constructor arg.
    vector v = { {a, b, c} }; // (1) Match a list constructor inside a vector constructor.
    vector v = { a, b, c}; // (2) No constructor, match an arg ( eg list ) constructor to then promote.

    // (3) Constructor match with arguments a, b, c only.
    vector v { a,b,c};

    // (4) Construct an arg ( ie list in this case) then a vector only.
    vector v { {a,b,c} };

    To me it is more elegant, logical, uniform. (2) is not optimal, but the non-assign syntax suggested above gives you more explicit worry-free control. Can anyone enlighten me as to the reasoning?

  25. @Tomek: I don’t know of a concrete proposal, though it’s sometimes discussed. This overlaps with C99-style designated initializers somewhat.

  26. @Herb Regarding multiple returns using tuple, I’ve tried to play with this before, but find dealing with tuples directly quite excruciating. The new std::get() syntax may help a little, but I really only enjoy using tuple returns when combined with std::tie at the call site, and std::tie cannot currently be used with auto inferred types.

    How much chance for success is there for a proposal such as:

    auto a, b;
    std::tie(a,b) = foo();

    i.e. supporting *very* delayed type deduction?

  27. @Herb Thanks. I really like that style, actually. I’m going to try to use it for a while and see where that takes me.

    However, I am already wishing auto could be used to declare multiple different deduced types per comma separated list of variable declarations, such as:

    auto v = vector{1,2,3}, l = list{1,2,3}, i = int{1}; // int{1} may be overkill

    This style of using c++ with auto feels yet closer to the comfort of using a dynamic language. huzzah!

  28. @Jay, @tr3w: Thanks for the example. I’ve added this to my notes to research further.

    @jd: Bingo, you’ve discovered C++’s new multiple return values feature: return a tuple. In Portland we said we intended to change tuple’s constructor to not be explicit, which means that return { 1, 3.14, “xyzzy” }; would work, but I didn’t I didn’t check yet though whether that got moved and approved in Bristol.

  29. @Michal: I didn’t want to freak people out with too much pushing of the proposed new “auto v = vector{ 1, 2, 3, 4 };” style, in part because it’s new and in part because I didn’t thoroughly motivate it in this GotW — that would have been a distraction and I plan to write another GotW that is all about auto. So in the (h) discussion I limited myself to encouraging readers to try it, and left it out of the final Guideline.

    But since you mention it, I’ve now added the “auto x =” form along with the more-traditional variable declaration form to the Guideline. Let’s see how many people freak out. :)

  30. What was the rational behind making `std::tuple`s constructor explicit thus blocking your recommended return syntax, such as:

    std::tuple t() { return { 6, 7 }; }

    Which increases with oddity when you consider that the syntax does work for a `std::pair`

  31. I wondering why C++14 don’t have named arguments? It would resolve some ambiguity ex.

    vector s {1,2,3,4,5,6} #constructor with std::initializer_list
    vector s {size=10, 0} #creates vector with ten elements initialized to 0.

    #this would create identical vectors
    vector v {begin=s.begin(), end=s.end()};
    vector v {s.begin(), s.end()};

    It should behave like normal function overloading, but when using named arguments first it should try to figure out proper order of parameter based on function declaration(parameter name in declaration).

    @Herb can say if there was such a proposal?

  32. I want to make sure I understand something:

    > When you design a class, avoid providing a constructor that ambiguously overloads with an initializer_list constructor, so your users are never forced to use ( ) to reach such a hidden constructor. (The standard library has such an overload only as a historical artifact retained for compatibility reasons, and should not be emulated in this detail.)

    If that’s true, why is it that Issue 2089 (http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3687.html#2089) doesn’t always use uniform initialization on types? Instead, the proposed resolution says to use some form of enable_if detection to see if something’s constructible with the parameters as constructor parameters. Only if it is *not* will it attempt to use a braced-init-list.

    That is trying to be a library solution for a LANGUAGE problem. That is the sort of thing that gave rise to `std::enable_if` and other such stupidity.

    We need a language solution for language problems.

    That “historical artifact” is not going to be going away. *Ever*. New containers like `dynarray` (http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3662.html#dynarray) have the *same* “historical artifacts”. So it’s not history: it’s the present day. It’s the future.

    It seems clear that the standards committee is really confused as to how to deal with this issue. Herb says it’s a “class design” issue, yet we are seeing new containers and new container proposals that *completely ignore* this supposed “class design” issue. There are no proposals that improve *existing* classes to allow you to access those constructors.

    And quite frankly, telling everyone to design their classes to avoid conflicts like this is like taking away explicit casting, then telling everyone to design their function overloads so that there can’t be any ambiguity.

    There’s a reason why the C++ compiler fails on an ambiguous function overload. But it doesn’t fail on an ambiguous uniform initialization; it just picks one and hides the other completely. That’s *wrong*.

  33. Thanks for the notes and guidelines, Herb.

    I think there’s a new line missing in the first code listing under answer 3.

    // oops, vexing parse [New Line Missing] complex …

  34. > […] the standard is explicit that an empty { } list means to call the default constructor if available.

    This seems counterintuitive to me, why should an empty list be any different than a non-empty one?

  35. Honestly, this is extremely disappointing. The committee has done little more than replace one vexing parse with another.

    Wouldn’t it have been less confusing to demand a syntax that distinguished between initializer lists and argument lists? E.g.

    vector v = { a, b, c}; // initializer list always
    vector v { a,b,c}; // constructor argument list always
    vector v {{a,b,c}}; // constructor argument list consisting of a single initializer_list (always)

  36. What about template classes/functions that wish to use classes that include initializer list? It’s quite cumbersome to remember about the initializer_list priority, which I also believe should be lower than the standard () overloads. Special exceptions in case of copy/default/(move?) just seem to exemplify the fact much more.

  37. During the transition (which will take a while since certain compilers still don’t support the initializer list syntax), we’re going to need programming (and blogging!) fonts that make a much sharper distinction between braces {} and parentheses ().

    Back at the dawn of ANSI C, I was working in a code base that allowed both K&R style functions and ANSI prototyped functions. Our compiler used different calling conventions depending on which style you used. That meant you had to be very careful about declaring ANSI-style prototypes for functions that took no parameters. If you said “int foo();”, the compiler assumed you meant K&R style. You had to say “int foo(void);” to make it clear you wanted the ANSI style. When I learned C++, I carried this habit along, as one of the texts I learned from said “C++ requires ANSI-style prototypes.” Many years later, someone berated me for typing unnecessary “void”s, and I dropped the habit. But it occurred to me at that time that if C++ had just _required_ the void in function declarations with no parameters, then it would have eliminated (most) of the most vexing parse problems.

  38. Thanks for a great revamp of the GotW series!

    It’s interesting that you recommend writing “auto w = Widget{x, y, z};” so that you don’t get bitten by otherwise forbidden explicit conversions. Scott Meyers’ latest Overview of the New C++11 (p.51) recommends: “Develop the habit of using brace initialization without “=“.” , which would mean writing “Widget w{x, y, z};” in this context.

    AFAICS, the only advantage of Scott’s recommendation is that it would also work for types that have neither a move nor a copy constructor (corner case, admittedly). Or do you see any other issues for choosing between your and Scott’s recommendation?

  39. Not sure if this is just an artifact of how it’s written, but it seems like you’re saying (g) requires an available copy-constructor, just like (f). While it’s true the latter does require one, the former is copy-list-initialization and it does not require one.

    ideone example

  40. Can you clarify about (a)? Was there change in the new standard regarding this?
    I’m talking about the case of

    struct widget
    {
    int i;
    };

    now

    widget w;

    was not the same as

    widget w = widget();

    (initialized vs. uninitialized member)

  41. The last guideline is nice, I just can’t really see it in practice. For example, how can we design a new vector class based on this?

  42. Jay, you are right, but the final advice is basically “dont do what vector does, its just a historical artifact”. Any type that accepts init_list should not also have complicated constructors, it seems.

    I envision vector could be patched with something like a tag parameter to differentiate:

    vector v = { std::size_init_tag(), 10 }; // now unambiguous

    Or a special size type:

    vector v = { std::size(10) }; // now unambiguous

    I think..

  43. Of course all of my angle brackets were stripped out by the blog software. Hopefully you can figure out where they are (hint, the vector holds objects of type “Foo” ).

  44. I think the comments by jmckesson to your original questions are worth further discussion and were glossed over here by labeling the issues around uniform initialization “extremely rare”. After reading it I wrote this test:

    #include 
    #include 
    
    struct Foo
    {
        Foo(int i) {} // #1
        Foo() {}
    };
    
    int main()
    {
        std::vector f {10};
    
        std::cout << f.size() << std::endl;
    }
    

    The above code, as written, emits “1” (10 is a converted to Foo by a constructor that takes an int, then the vector’s initializer_list constructor is called). If I comment out the line commented as #1, the result is “10” (the initializer_list cannot be converted so the int constructor is used). The behavior of the vector constructor changes depending on the type it holds. This seems like a really big deal and makes me nervous about using uniform initialization.

    I feel like this must have come up in discussions before. Can you comment on the issue?

  45. Your final guideline clears up my main annoyance with these new rules (though I dislike that default/copy/move constructors are special cased to bind tighter than init_list constructors, and think init_list should just have bound least tight always).

    However, I have a question about seemingly conflicting advice here (Guideline in Q1(h) and Guideline Q4):

    “[use] auto w = widget{x};” vs “Guideline: Prefer to use initialization with { }, such as vector v = { 1, 2, 3, 4 };”

    Shouldn’t then that last example be auto v = vector{1, 2, 3, 4}; given the way the rules are written?

Comments are closed.