C++11中的数组声明和初始化 [英] Array declaration and initialization in C++11

查看:22
本文介绍了C++11中的数组声明和初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里有 8 种在 C++11 中声明和初始化数组的方法,在 g++ 下似乎没问题:

Here are 8 ways to declare and initialize arrays in C++11 that seems ok under 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}});

根据严格标准(以及即将推出的 C++14 标准),正确的标准是什么?什么是最常见/最常用的以及应该避免的(以及出于什么原因)?

What are the correct ones according to the strict standard (and the upcoming C++14 standard) ? What are the most common/used and those to avoid (and for what reason) ?

推荐答案

C++11 总结/TL;DR

C++11 summary / TL;DR

  • 由于大括号省略缺陷,示例 0、2、6 不需要工作.然而,最新版本的编译器实现了针对该缺陷提出的解决方案,因此这些示例可以正常工作.
  • 由于未指定 std::array 是否包含原始数组.因此,示例 1、3、5、7 不需要工作.但是,我不知道在哪些标准库实现中它们不起作用(在实践中).
  • 示例 4 将始终有效:std::array<int, 3>arr4 = {1, 2, 3};
  • Due to the brace elision defect, examples 0, 2, 6 are not required to work. Recent version of compilers however implement the proposed resolution for that defect, so that these examples will work.
  • As it is unspecified whether std::array contains a raw array. Therefore, examples 1, 3, 5, 7 are not required to work. However, I do not know of a Standard Library implementation where they do not work (in practice).
  • Example 4 will always work: std::array<int, 3> arr4 = {1, 2, 3};

我更喜欢版本 4 或版本 2(带有大括号省略修复),因为它们直接初始化并且需要/可能工作.

I'd prefer version 4 or version 2 (with the brace elision fix), since they initialize directly and are required/likely to work.

对于 Sutter 的 AAA 样式,您可以使用 auto arrAAA = std::array<int, 3>{1, 2, 3};,但这需要大括号省略修复.

For Sutter's AAA style, you can use auto arrAAA = std::array<int, 3>{1, 2, 3};, but this requires the brace elision fix.

std::array 必须是一个聚合 [array.overview]/2,这意味着它没有用户提供的构造函数(即只有默认、复制、移动 ctor).

std::array is required to be an aggregate [array.overview]/2, this implies it has no user-provided constructors (i.e. only default, copy, move ctor).

std::array<int, 3> arr0({1, 2, 3});
std::array<int, 3> arr1({{1, 2, 3}});

(..) 的初始化是直接初始化.这需要构造函数调用.在 arr0arr1 的情况下,只有复制/移动构造函数是可行的.因此,这两个示例意味着从花括号初始化列表中创建一个临时std::array,并将其复制/移动到目标.通过复制/移动省略,编译器被允许省略该复制/移动操作,即使它有副作用.

An initialization with (..) is direct-initialization. This requires a constructor call. In the case of arr0 and arr1, only the copy/move constructor are viable. Therefore, those two examples mean create a temporary std::array from the braced-init-list, and copy/move it to the destination. Through copy/move elision, the compiler is allowed to elide that copy/move operation, even if it has side effects.

注意即使临时对象是纯右值,它也可能调用一个副本(语义上,在复制省略之前),因为 std::array 的移动 ctor 可能没有被隐式声明,例如如果它被删除了.

N.B. even though the temporaries are prvalues, it might invoke a copy (semantically, before copy elision) as the move ctor of std::array might not be implicitly declared, e.g. if it were deleted.

std::array<int, 3> arr6 = std::array<int, 3>({1, 2, 3});
std::array<int, 3> arr7 = std::array<int, 3>({{1, 2, 3}});

这些是复制初始化的示例.创建了两个临时对象:

These are examples of copy-initialization. There are two temporaries created:

  • 通过braced-init-list {1, 2, 3}调用复制/移动构造函数
  • 通过表达式std::array(..)
  • through the braced-init-list {1, 2, 3} to call the copy/move constructor
  • through the expression std::array<int, 3>(..)

后一个临时变量然后被复制/移动到命名的目标变量.可以省略两个临时对象的创建.

the latter temporary then is copied/moved to the named destination variable. The creation of both temporaries can be elided.

据我所知,一个实现可以编写一个 explicit array(array const&) = default; 构造函数并且 not 违反标准;这将使这些示例格式不正确.([container.requirements.general] 排除了这种可能性,感谢 David Krauss,请参阅 本次讨论.)

As far as I know, an implementation could write an explicit array(array const&) = default; constructor and not violate the Standard; this would make those examples ill-formed. (This possibility is ruled out by [container.requirements.general], kudos to David Krauss, see this discussion.)

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}};

这是聚合初始化.它们都直接"初始化 std::array,而不调用 std::array 的构造函数,也没有(语义上)创建临时数组.std::array 的成员通过复制初始化进行初始化(见下文).

This is aggregate-initialization. They all "directly" initialize the std::array, without calling a constructor of std::array and without (semantically) creating a temporary array. The members of the std::array are initialized via copy-initialization (see below).

关于大括号省略的话题:

On the topic of brace-elision:

在 C++11 标准中,大括号省略仅适用于 T x = { a }; 形式的声明,但不适用于 T x { a };.这是被认为是一个缺陷在 C++1y 中修复,但是建议的解决方案不是标准的一部分(DRWP 状态,请参见链接页面的顶部),因此您不能指望您的编译器也为 T x { a };.

In the C++11 Standard, brace elision only applies to declarations of the form T x = { a }; but not to T x { a };. This is considered a defect and will be fixed in C++1y, however the proposed resolution is not part of the Standard (DRWP status, see top of the linked page) and therefore you cannot count on your compiler implementing it also for T x { a };.

因此,std::array;arr2{1, 2, 3}; (示例 0, 2, 6)严格来说是格式错误的.据我所知,最新版本的 clang++ 和 g++ 已经允许在 T x { a }; 中使用大括号省略.

Therefore, std::array<int, 3> arr2{1, 2, 3}; (examples 0, 2, 6) are ill-formed, strictly speaking. As far as I know, recent versions of clang++ and g++ allow the brace elision in T x { a }; already.

在示例 6 中,std::array<int, 3>({1, 2, 3}) 使用复制初始化:参数传递的初始化也是复制初始化.然而,大括号省略的缺陷限制,T x = { a };" 形式的声明中,也不允许大括号省略用于参数传递,因为它不是声明,当然不是那种形式.

In example 6, std::array<int, 3>({1, 2, 3}) uses copy-initialization: the initialization for argument passing is also copy-init. The defective restriction of brace elision however, "In a declaration of the form T x = { a };", also disallows brace elision for argument passing, since it's not a declaration and certainly not of that form.

关于聚合初始化的话题:

On the topic of aggregate-initialization:

正如 Johannes Schaub 指出 在评论中只有保证你可以初始化一个std::array 具有以下语法 [array.overview]/2:

As Johannes Schaub points out in a comment, it is only guaranteed that you can initialize a std::array with the following syntax [array.overview]/2:

array<T, N> a = { initializer-list };

您可以由此推断,if 允许以 T x { a }; 的形式使用大括号省略,语法

You can deduce from that, if brace-elision is allowed in the form T x { a };, that the syntax

array<T, N> a { initializer-list };

格式正确,含义相同.但是,不能保证 std::array 实际上包含一个原始数组作为其唯一的数据成员(另见 LWG 2310).我认为一个例子可能是部分特化 std::array<T, 2>,其中有两个数据成员 T m0T m1.因此,不能断定

is well-formed and has the same meaning. However, it is not guaranteed that std::array actually contains a raw array as its only data member (also see LWG 2310). I think one example could be a partial specialization std::array<T, 2>, where there are two data members T m0 and T m1. Therefore, one cannot conclude that

array<T, N> a {{ initializer-list }};

格式正确.不幸的是,这会导致无法保证为 T x { a }; 初始化一个 std::array 临时的不带括号省略的方法,这也意味着奇数示例(1、3、5、7)不需要工作.

is well-formed. This unfortunately leads to the situation that there's no guaranteed way of initializing a std::array temporary w/o brace elision for T x { a };, and also means that the odd examples (1, 3, 5, 7) are not required to work.

所有这些初始化 std::array 的方法最终都会导致聚合初始化.它被定义为聚合成员的复制初始化.但是,使用花括号初始化列表的复制初始化仍然可以直接初始化聚合成员.例如:

All of these ways to initialize a std::array eventually lead to aggregate-initialization. It is defined as copy-initialization of the aggregate members. However, copy-initialization using a braced-init-list can still directly initialize an aggregate member. For example:

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

第一个尝试分别从初始化子句 12 初始化数组元素.这个复制初始化等价于 foo arr0_0 = 1; 反过来又等价于 foo arr0_0 = foo(1); 这是非法的(已删除的 copy-ctor).

The first tries to initialize the array elements from the initializer-clauses 1 and 2, respectively. This copy-initialization is equivalent to foo arr0_0 = 1; which in turn is equivalent to foo arr0_0 = foo(1); which is illegal (deleted copy-ctor).

第二个不包含表达式列表,而是初始化器列表,因此它不满足 [array.overview]/2 的要求.在实践中,std::array 包含一个原始数组数据成员,它将(仅)从第一个初始化子句 {1}、第二个子句 {2} 那么是非法的.

The second does not contain a list of expressions, but a list of initializers, therefore it doesn't fulfil the requirements of [array.overview]/2. In practice, std::array contains a raw array data member, which would be initialized (only) from the first initializer-clause {1}, the second clause {2} then is illegal.

第三个与第二个有相反的问题:如果存在 一个数组数据成员,它可以工作,但不能保证.

The third has the opposite problem as the second: It works if there is an array data member, but that isn't guaranteed.

这篇关于C++11中的数组声明和初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆