std::array初始化代碼編譯出現too many initializers錯誤的原因?

如下面代碼所示,array& a1 = {{"a"}, {"b"}};會編譯不通過,提示:

std::array&』的初始值設定項太多
array& a1 = {{"a"}, {"b"}}; compile error: to many initializers

但是&其他類型&有的類型的初始化可以去掉最外層的大括弧。array&用gcc編譯出錯但是VS2015不會報錯。請問是什麼原因?

測試環境Windows 10 Pro x64 visual studio 2015 / Cgywin x86_64 g++ 5.4.0 -std=c++11

#include &
#include &
#include &
#include &
#include &
#include &
using namespace std;

struct Person
{
string name;
};
bool operator==(const Person l, const Person r) { return l.name == r.name; }

#define CHECK_EQUAL(x, y) do {
cout &<&< #x " == " #y " : " &<&< ((x == y) ? "true" : "false") &<&< endl; } while (false) int main() { int i1 = 123; int i2 = { 123 }; CHECK_EQUAL(i1, i2); //i1 == i2 : true Person p1 = { "a" }; Person p2 = { { "a" } }; CHECK_EQUAL(p1, p2); //p1 == p2 : true //array& a1 = {{"a"}, {"b"}}; //compile error: to many initializers
array& a2 = { { { "a" },{ "b" } } };
array& a3 = { { { { "a" } },{ { "b" } } } };
CHECK_EQUAL(a2, a3); //a2 == a3 : true

#ifdef WIN32
array& a4 = {1, 2}; //gcc compile error: to many initializers
array& a5 = { { 1, 2 } };
array& a6 = {{{1}, {2}}}; //gcc compile error: 類型『int』的標量初始化帶花括弧
CHECK_EQUAL(a4, a5); //a7 == a8 : true
CHECK_EQUAL(a4, a6); //a8 == a9 : true
#endif

array& a7 = { "1", "2" };
array& a8 = { { "1", "2" } };
array& a9 = { { { "1" },{ "2" } } };
CHECK_EQUAL(a7, a8); //a7 == a8 : true
CHECK_EQUAL(a8, a9); //a8 == a9 : true

vector& v1 = { { "a" },{ "b" } };
vector& v2 = { { { "a" } },{ { "b" } } };
vector& v3 = { { { { "a" } },{ { "b" } } } };
CHECK_EQUAL(v1, v2); //v1 == v2 : true
CHECK_EQUAL(v1, v3); //v1 == v3 : true
return 0;
}


在C++11引入initializer_list後,由於C++11標準(§8.5.1/11)中規定

In a declaration of the form
T x = { a };
braces can be elided in an initializer-list as follows.

所以當出現

T x = { a };

的時候,有這麼幾種情況:

1.T是一個聚合類

這種情況下,程序會把大括弧里的值按類內成員變數的定義順序依次賦值,如果參數過多,會報錯「too many initializers」

struct node {
int a, b;
};
int main() {
node c = {1, 2}; // OK, c.a = 1, c.b = 2
node d = {1, 2, 3}; // error: too many initializers for node
}

2.T有用戶自定義的構造函數

這時候,這句話等價於

T x(a);

也就是說,程序會調用T里和a相匹配的構造函數來構造x

#include &
struct node {
int a, b;
node (int a) {
this-&>a = a;
this-&>b = 2;
}
};
int main() {
node s = {1};
std::cout &<&< s.a &<&< &<&< s.b; // 1 2 }

3.T有一個以initializer_list為參數的構造函數

這時候,根據一開始提到的標準里的規定,事實上,這句話等價於

T x = {{a}};

和2一樣,程序會調用T里以initializer_list為參數的那個構造函數來構造x,其中initializer_list里的參數為a

#include &
#include &
struct node {
int a, b;
node (const std::initializer_list& c) {
this-&>a = 0;
this-&>b = 0;
for (auto i : c) {
this-&>a = this-&>b;
this-&>b = i;
}
}
};
int main() {
node s = {1, 2, 3, 4, 5};
std::cout &<&< s.a &<&< &<&< s.b; // 4 5 }

4.2和3同時存在

T里既有一個以initializer_list為參數的構造函數,也有另外的用戶自定義的構造函數,這時候,程序優先按3來構造,典型的例子就是vector

vector& a = {1, 2};

這樣的初始化是讓a里一開始就有2個元素1、2,而不是只有1個為2的元素

===

這是4種比較基本的情況,但事實上,還有可能有更多的奇怪的東西,就比如現在提出的這個問題

在說這個問題之前,首先要指出的是,array和其他容器並不一樣,它是一個聚合類,也就是說,它沒有用戶自定義的構造函數,所以分析的時候得按1來處理,你可以把它看成

template&
class array {
public:
T __array[len];
.... // 一些成員函數
};

然後回到問題來,一個一個看

===

array& a1 = {{"a"}, {"b"}}; //compile error: too many initializers

根據1,它會把{"a"}, {"b"}按定義順序依次給array里的成員變數賦值,它在把{"a"}賦值給__array後(__array[2] = {"a"};),對於{"b"},它找不到可以賦值的成員,所以會報錯

不過你有可能會問,既然它是聚合類,那為什麼還可以直接像下面這樣子呢?

array& a7 = { "1", "2" };

還是根據一開始提到的標準里的規定,所以大括弧省略掉了,具體例子可以看給出的參考的第一個鏈接,其實就和C語言里定義二維數組一樣,你可以寫成

int a[2][3] = {{1, 2, 3}, {4, 5, 6}};

也可以不寫裡面的大括弧,直接寫

int a[2][3] = {1, 2, 3, 4, 5, 6};

而題主說

但是其他類型的初始化可以去掉最外層的大括弧

事實上其他類型也是不行的,只是你寫的和這個不一樣所以沒報錯,你要是寫成和這個一樣的形式:

array& a7 = { {"1"}, {"2"} };

也是會報錯的

===

array& a4 = {1, 2}; //gcc compile error: to many initializers

在我電腦上沒有出現報錯,按理來說應該也不會報錯,題主可以提供一下更詳細的信息

===

array& a6 = {{{1}, {2}}}; //gcc compile error: 類型『int』的標量初始化帶花括弧

這個我也沒有查到具體的原因,但經測試發現,只有基本類型會報錯,而且既然VS2015不會報錯,而gcc報錯,那這有可能是個UB

參考:

When can outer braces be omitted in an initializer list?

Why is the C++ initializer_list behavior for std::vector and std::array different?

Ignoring GCC amp;quot;error: braces around scalar initializer for typeamp;quot; errors. Make them warnings


推薦閱讀:

【遊戲框架系列】用C++畫光(二)——矩形
使用 AsyncListUtil 優化 RecyclerView
《奧日與黑暗森林》這樣的遊戲主要需要哪些技術,幾個人的小團隊能實現嗎?
[譯] 閱讀 NodeJS 文檔,我學到了這 19 件事情
【偽教程】手把手教你成為matlab作曲家

TAG:編程 | 代碼 | C | CC | C11 |