1

我最近意识到,在 C++11 中,我们可以调用委托初始化器列表构造函数,例如

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

这个语法正确吗?似乎是,虽然我本来希望在调用函数时总是使用括号,比如Foo({42}). 下面的示例代码在 clang++ 和 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;
}

我很清楚统一初始化,比如使用声明对象{ },但不知道我们也可以调用构造函数。但是我们不能调用函数,下面的代码不能编译

#include <initializer_list>

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

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

所以,总而言之,我的问题如下:委派构造函数时是否有特殊规则,允许仅使用大括号调用 init-list 构造函数,例如Foo{something}

4

2 回答 2

5

是的,mem-initializer可以Foo{42}包含带括号的表达式列表或花括号初始化列表。无论mem-initializer-id是表示构造函数的类、基类还是成员,情况都是如此:也就是说,无论是构造函数委托还是不委托。请参阅 [class.base.init] 中的语法。

此外,该标准规定([class.base.init]/7 in C++14)由表达式列表或花括号初始化列表进行的初始化是根据通常的初始化规则进行的。因此,如果初始化程序是一个花括号初始化列表,那么std::initializer_list构造函数将在重载决议中受到青睐。

于 2015-04-22T00:00:23.057 回答
3

我认为规则很清楚,您将被允许委托给初始化列表构造函数(强调我的):

如果类本身的名称在成员初始化器列表中显示为类或标识符,则列表必须仅包含一个成员初始化器;这样的构造函数称为委托构造函数,初始化器列表中唯一成员选择的构造函数就是目标构造函数。这种情况下,目标构造函数通过重载决议选择并首先执行,然后控制权返回委托构造函数,它的身体被执行。

因此,通过重载决议,您可以调用您的初始化列表构造函数,就像您在“正常”代码中调用它一样,因为。

但是,我不知道任何应该允许调用接受初始值设定项列表的函数的方法,就像您可以调用构造函数一样。

编辑:关于构造函数规则的更多信息(再次强调我的):

任何构造函数的函数定义的主体,在复合语句的左大括号之前,可以包括成员初始化器列表,其语法是冒号字符 :,后跟一个或多个成员初始化器的逗号分隔列表,每个其中具有以下语法
     class-or-identifier (expression-list(optional) ) (1)
     class-or-identifier brac-init-list (2) (C++11 起)
      parameter-pack ... (3 ) (C++11 起)

1) 使用直接初始化初始化类或标识符命名的基类或成员,或者,如果表达式列表为空,则值初始化
2)使用列表初始化初始化类或标识符命名的基类或成员(变为如果列表为空,则进行值初始化,在初始化聚合时进行聚合初始化)
3)使用包扩展初始化多个基

所以根据#2,它似乎是合法的。

于 2015-04-22T00:01:31.520 回答