Vin*_*ent 11 c++ initialization c++11 stdarray c++14
以下是在C++ 11中声明和初始化数组的8种方法g++
:
/*0*/ std::array<int, 3> arr0({1, 2, 3});
/*1*/ std::array<int, 3> arr1({{1, 2, 3}});
/*2*/ std::array<int, 3> arr2{1, 2, 3};
/*3*/ std::array<int, 3> arr3{{1, 2, 3}};
/*4*/ std::array<int, 3> arr4 = {1, 2, 3};
/*5*/ std::array<int, 3> arr5 = {{1, 2, 3}};
/*6*/ std::array<int, 3> arr6 = std::array<int, 3>({1, 2, 3});
/*7*/ std::array<int, 3> arr7 = std::array<int, 3>({{1, 2, 3}});
Run Code Online (Sandbox Code Playgroud)
根据严格标准(以及即将推出的C++ 14标准),正确的是什么?什么是最常见的/使用的和那些要避免的(以及为什么)?
dyp*_*dyp 19
C++ 11 summary/TL; DR
std::array
包含原始数组.因此,不需要实施例1,3,5,7.但是,我不知道标准库实现它们不起作用(实际上).std::array<int, 3> arr4 = {1, 2, 3};
我更喜欢版本4或版本2(使用大括号省略),因为它们直接初始化并且需要/可能有效.
对于Sutter的AAA风格,你可以使用auto arrAAA = std::array<int, 3>{1, 2, 3};
,但这需要支撑elision修复.
std::array
需要是一个聚合[array.overview]/2,这意味着它没有用户提供的构造函数(即只有默认,复制,移动ctor).
std::array<int, 3> arr0({1, 2, 3});
std::array<int, 3> arr1({{1, 2, 3}});
Run Code Online (Sandbox Code Playgroud)
初始化(..)
是直接初始化.这需要构造函数调用.在的情况下,arr0
和arr1
,只有复制/移动的构造是可行的.因此,这两个示例意味着从braced-init-list 创建临时std::array
,并将其复制/移动到目标.通过复制/移动省略,允许编译器忽略该复制/移动操作,即使它有副作用.
注意,即使临时值是prvalues,它也可能会调用副本(在语义上,在复制省略之前),因为std::array
可能不会隐式声明移动ctor ,例如,如果它被删除.
std::array<int, 3> arr6 = std::array<int, 3>({1, 2, 3});
std::array<int, 3> arr7 = std::array<int, 3>({{1, 2, 3}});
Run Code Online (Sandbox Code Playgroud)
这些是复制初始化的示例.有两个临时创建:
{1, 2, 3}
调用复制/移动构造函数std::array<int, 3>(..)
然后将临时的临时复制/移动到指定的目标变量.两个临时工的创造都可以省略.
据我所知,一个实现可以写一个([container.requirements.general]排除了这种可能性,对David Krauss表示赞赏,请参阅此讨论.)explicit array(array const&) = default;
构造函数,而不是违反标准; 这会使这些例子形成不良.
std::array<int, 3> arr2{1, 2, 3};
std::array<int, 3> arr3{{1, 2, 3}};
std::array<int, 3> arr4 = {1, 2, 3};
std::array<int, 3> arr5 = {{1, 2, 3}};
Run Code Online (Sandbox Code Playgroud)
这是聚合初始化.它们都"直接"初始化std::array
,而无需调用构造函数(std::array
无论是否)(语义上)创建临时数组.的成员std::array
通过复制初始化初始化(见下文).
关于大括号的主题:
在C++ 11标准中,大括号仅适用于表单的声明T x = { a };
但不适用于T x { a };
.这被认为是一个缺陷,将在C++ 1y中修复,但是建议的分辨率不是标准的一部分(DRWP状态,请参见链接页面的顶部),因此您也不能指望您的编译器实现它T x { a };
.
因此,std::array<int, 3> arr2{1, 2, 3};
严格来说,(实例0,2,6)是不正确的.据我所知,最新版本的clang ++和g ++ T x { a };
已经允许使用大括号.
在示例6中,std::array<int, 3>({1, 2, 3})
使用复制初始化:参数传递的初始化也是copy-init.但是,对于括号中的缺陷限制,"在形式的声明中T x = { a };
",也不允许使用括号来进行论证传递,因为它不是声明,当然也不是那种形式.
关于聚合初始化的主题:
由于约翰内斯绍布指出的评论,它只能保证你可以初始化std::array
以下语法[array.overview/2:
array<T, N> a = { initializer-list };
您可以从中推断,如果表单中允许使用大括号T x { a };
,则表示语法
array<T, N> a { initializer-list };
形式良好,具有相同的含义.但是,不能保证std::array
实际上包含一个原始数组作为其唯一的数据成员(另见LWG 2310).我认为一个例子可能是部分专业化std::array<T, 2>
,其中有两个数据成员T m0
和T m1
.因此,人们无法得出结论
array<T, N> a {{ initializer-list }};
结构良好.不幸的是,这导致无法保证初始化std::array
临时w/o括号省略的情况T x { a };
,并且还意味着奇数示例(1,3,5,7)不需要工作.
所有这些初始化的方法std::array
最终都会导致聚合初始化.它被定义为聚合成员的复制初始化.但是,使用braced-init-list进行复制初始化仍然可以直接初始化聚合成员.例如:
struct foo { foo(int); foo(foo const&)=delete; };
std::array<foo, 2> arr0 = {1, 2}; // error: deleted copy-ctor
std::array<foo, 2> arr1 = {{1}, {2}}; // error/ill-formed, cannot initialize a
// possible member array from {1}
// (and too many initializers)
std::array<foo, 2> arr2 = {{{1}, {2}}}; // not guaranteed to work
Run Code Online (Sandbox Code Playgroud)
第一个尝试分别从初始化子句1
和子句初始化数组元素2
.这种复制初始化相当于foo arr0_0 = 1;
其相当于foo arr0_0 = foo(1);
非法(删除的复制器).
第二个不包含表达式列表,而是一个初始化器列表,因此它不满足[array.overview]/2的要求.实际上,std::array
包含一个原始数组数据成员,它将从第一个初始化子句初始化(仅){1}
,{2}
然后第二个子句是非法的.
第三具有相反的问题,因为第二:它的工作原理,如果有是一个数组数据成员,但是没有保证.
归档时间: |
|
查看次数: |
5394 次 |
最近记录: |