Question

Using {} instead of () in a constructor will allow me to initialize class members with a specific constructor right in the header, like so:

class X {
    private:
        std::vector y{1, 2, 3};
};

But how do I know that for a class Z I use Z z{a, b}; will call the constructor that has two parameters — Z::Z(int a, int b) — and not the one with the std::initializer_list?

I mean std::complex(1, 2) and std::complex{1, 2} are the same, but std::vector(3) and std::vector{3} are certainly not.

Should I always use the {} variant or use () unless I need {}?

Was it helpful?

Solution

This is a topic that can span an entire book chapter. See a quote from this recent draft Item by Scott Meyers of his upcoming Effective Modern C++ (reformatted for clarity):

Most developers end up choosing one kind of delimiter as a default, using the other only when they have to.

  • Braces-by-default folks are attracted by their wide applicability, their prevention of narrowing conversions, and their avoidance of C++’s most vexing parse. Such folks understand that in some cases (e.g., creation of a std::vector with a given size and initial element value), parentheses are required.

  • In contrast, the go-parentheses-go crowd embraces parentheses as their default argument delimiter. They’re attracted to its consistency with the C++98 syntactic tradition, its avoidance of the auto-deduced-a-std::initializer_list problem, and the knowledge that their object creation calls won’t be inadvertently waylaid by std::initializer_list constructors. They concede that sometimes only braces will do (e.g., when creating a container with particular values).

Neither approach is rigorously better than the other. My advice is to pick one and apply it consistently.

OTHER TIPS

The rule is that if a class has a constructor that takes an initializer_list, and the elements in your braced-init-list are convertible (remember that braced-init-lists do not allow narrowing conversions) to the initializer_list's argument type, then the constructor that takes the initializer_list will always be called. Otherwise the compiler will attempt to match other constructors.

In case of an empty braced-init-list the default constructor is called, assuming one is accessible.

There is no simple answer to your question about whether parentheses should be preferred over braces, or vice versa, for initialization. In hindsight, vector's constructors are a bad design choice because it can be made to behave unexpectedly, at least to the uninitiated, quite trivially.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top