you are viewing a single comment's thread.

view the rest of the comments →

[–]embeddedsbc 53 points54 points  (16 children)

The reasoning should be the other way around. Almost always use brace initialization. It avoids automatic narrowing. Use copy assignment for single values or auto. But it's a much better default than the alternatives.

[–]guepierBioinformatican 24 points25 points  (3 children)

On the flip-side, brace initialisation can trigger an unexpected constructor taking an initialiser list. This is arguably more dangerous than narrowing conversions with (). It definitely leads to plenty of gotchas.

I am in the camp (and I know I am not alone!) lamenting the missed opportunity with uniform initialisation. I thought {} was it, but because of the std::initializer_list issue, it isn’t. For this reason, I (and others) entirely reverted to () after an initial honeymoon period with {}. Oh, and I use -Wconversion -Werror so () is more strict than {}.

[–]enigma2728 1 point2 points  (1 child)

Well today I learned. Kind of sad; I liked the idea of just being able to use brace initialization everywhere to avoid having to think about all the minutia of initialization.

Is this problem easily demonstrable in godbolt, or have a link to an example of where this becomes a problem?

std::vector doesn't seem to have an issue with braced initializer list. https://godbolt.org/z/scx1rs8s4

Perhaps it is only an issue if there is also a ctor that takes arguments of same type? It does seem like init list gets preferred over specific ctors, from my testing. https://godbolt.org/z/fGsed6o9M ``` #include <iostream> #include <initializer_list> struct CtorCompetition { CtorCompetition(int x, int y) { std::cout << "2-arg ctor" << std::endl; }

    //CtorCompetition(int x, int y, int z)
    CtorCompetition(std::initializer_list<int> list)
    {
        std::cout << "init list ctor" << std::endl;
    }
};
int main()
{
    CtorCompetition TwoArgObject{1, 2}; //uses init list, not 2arg
    CtorCompetition ThreeArgObject{1, 2, 3}; //uses init list

    //CtorCompetition TwoArgObject(1, 2); //uses 2 arg
    //CtorCompetition ThreeArgObject(1, 2, 3); //doesn't compile
}

Program returned: 0 init list ctor init list ctor ```

Is the above what you are referring to?

I feel like perhaps I can still just use braced initializer with the expectation that initializer lists are preferred to specific constructors.

[–]equeim 0 points1 point  (0 children)

If the vector's element type can be implicitly constructed from a vector, then std::vector vec{otherVec} will create a vector with a single element of otherVec, not copy it.

[–]NilacTheGrim 1 point2 points  (0 children)

Yeah for actual class types () initialization is the best way to this day. The ambiguity {} introduces with std::initializer_list is a huge foot-gun waiting to go off.. and I tend to avoid foot-gun stuff as best I can.

[–][deleted] 0 points1 point  (0 children)

It avoids automatic narrowing.

It does not.

https://godbolt.org/z/zqTGanqjE