考虑以下示例以了解initializer-list-constructor的用法:
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的构造函数的调用混淆。同样,第一种样式看起来对其他编程语言也很熟悉。
最佳答案
如果vector
为string
,则这三种形式之间没有区别。但是,如果采用initializer_list
的构造函数为explicit
,则前两者之间可能会有所不同。在这种情况下,不允许使用第一个(即复制列表初始化),而允许使用其他两个(即直接列表初始化)。
因此,我更喜欢第三种形式。我会避免第二个,因为括号是多余的。
正如Yakk在注释中指出的那样,当正在构造的类型没有采用initializer_list
的构造函数时,还会出现进一步的差异。
例如,正在构造的类型具有一个构造函数,该构造函数带有3个参数(均为char const *
类型),而不是initializer_list
构造函数。在这种情况下,格式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。关于c++ - 调用Initializer-List-Constructor的不同方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25120659/