问题
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}
?
回答1:
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.
回答2:
I think the rule is pretty clear that you will be allowed to delegate to an initializer list constructor (emphasis mine):
If the name of the class itself appears as class-or-identifier in the member initializer list, then the list must consist of that one member initializer only; such constructor is known as the delegating constructor, and the constructor selected by the only member of the initializer list is the target constructor In this case, the target constructor is selected by overload resolution and executed first, then the control returns to the delegating constructor and its body is executed.
So by overload resolution, you can call your initializer list constructor just as if you were calling it in 'normal' code because.
However, I don't know of anything that should allow calling a function that accepts an initializer list in the same way that you can call a constructor with one.
Edit: More about constructor rules (Emphasis again mine):
The body of a function definition of any constructor, before the opening brace of the compound statement, may include the member initializer list, whose syntax is the colon character :, followed by the comma-separated list of one or more member-initializers, each of which has the following syntax
class-or-identifier (expression-list(optional) ) (1)
class-or-identifier brace-init-list (2) (since C++11)
parameter-pack ... (3) (since C++11)1) Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization
2) Initializes the base or member named by class-or-identifier using list-initialization (which becomes value-initialization if the list is empty and aggregate-initialization when initializing an aggregate)
3) Initializes multiple bases using a pack expansion
So according to #2, it appears it's legal.
来源:https://stackoverflow.com/questions/29785223/calling-constructor-with-braces-instead-parantheses