Search code examples
c++c++11constructorinitializer-listdelegating-constructor

Calling constructor with braces instead parantheses


I recently realized that in C++11 we can call a delegating initializer-list constructor like

Foo() : Foo{42} // delegate to Foo(initializer_list<>)

Is this syntax correct? It seems to be, although I would have expected to always use parentheses when calling a function, like Foo({42}). The example code below compiles fine in both clang++ and g++

#include <iostream>
#include <initializer_list>

struct Foo
{
    Foo() : Foo{42} // I would have expected invalid syntax, use Foo({42})
    {
        std::cout << "Foo()... delegating constructor\n";
    }
    Foo(std::initializer_list<int>)
    {
        std::cout << "Foo(initializer_list)\n";
    }
};

int main()
{
    Foo foo;
}

I am well aware of uniform initialization, like declaring objects using { }, but did not know we can also call constructors. We cannot call functions though, the following doesn't compile:

#include <initializer_list>

void f(std::initializer_list<int>){}

int main()
{
    f{5}; // compile time error, must use f({5})
}

So, to summarize, my question is the following: are there special rules when delegating constructors, that allow for calling a init-list constructor using only braces, like Foo{something}?


Solution

  • Yes, a mem-initializer such as Foo{42} can contain either a parenthesized expression-list or a braced-init-list. This is the case regardless of whether the mem-initializer-id denotes the constructor's class, a base class, or a member: that is, both when the constructor delegates and when it does not. See the grammar in [class.base.init].

    Furthermore, the standard specifies ([class.base.init]/7 in C++14) that the initialization by the expression-list or braced-init-list occurs according to the usual rules of initialization. Therefore if the initializer is a braced-init-list then std::initializer_list constructors will be favoured in overload resolution.