调用initializer-list-constructor的不同方法

时间:2014-08-04 14:07:57

标签: c++ c++11 initialization-list

考虑初始化列表构造函数用法的这个例子:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v({ "xyzzy", "plugh", "abracadabra" });
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" }; 

他们之间是否有任何差异(甚至是轻微的)?

在一个大项目中,你必须定义一个标准,你会选择哪种风格? 我更喜欢第一种风格,第三种风格很容易与使用args的构造函数调用混淆。第一种风格看起来也很熟悉其他编程语言。

1 个答案:

答案 0 :(得分:7)

如果vectorstring,则三种形式之间没有区别。但是,如果构造函数采用initializer_listexplicit,则第一个和另外两个之间可能存在差异。在这种情况下,第一个是 copy-list-initialization ,是不允许的,而另外两个是 direct-list-initialization ,是允许的。< / p>

由于这个原因,我的偏好是第三种形式。我会避免第二个,因为括号是多余的。


正如Yakk在评论中指出的那样,当正在构造的类型没有构造函数采用initializer_list时,会出现进一步的差异。

例如,假设正在构造的类型有一个构造函数,它接受3个参数,所有类型都是char const *,而不是initializer_list构造函数。在这种情况下,表格1和表格1 3是有效的,但是2是格式错误的,因为当括在括号中时,braced-init-list与3参数构造函数不匹配。

如果类型确实有初始化列表构造函数,但是braced-init-list的元素不能隐式转换为initializer_list<T>,那么将考虑其他构造函数。假设存在另一个匹配的构造函数,表单2导致构造一个中间副本,而另外两个不构造。这可以通过使用-fno-elide-constructors编译的following example来证明。

struct foo
{
    foo(int, int) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    foo(foo const&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    foo(std::initializer_list<std::string>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

int main()
{
    foo f1 = {1,2};
    std::cout << "----\n";
    foo f2({1,2});
    std::cout << "----\n";
    foo f3{1,2};
}

输出:

foo::foo(int, int)
----
foo::foo(int, int)
foo::foo(const foo&)
----
foo::foo(int, int)


以下案例不是问题的一部分,但仍需要注意。在某些情况下,使用嵌套大括号会导致行为不直观。考虑

std::vector<std::string> v1{{ "xyzzy", "plugh", "abracadabra" }};
std::vector<std::string> v2{{ "xyzzy", "plugh"}};

v1按预期工作,将是包含3个字符串的vector,而v2会导致未定义的行为。有关详细说明,请参阅this answer