終点枕崎

プログラミングとvimと音楽と地理

std::arrayの初期化

次のコードをclangで-Wallでコンパイルすると警告が出ます。gccでは出ないです。

clangのバージョン : Apple LLVM version 9.0.0 (clang-900.0.37)

[test1.cpp]

#include <array>
#include <iostream>

int main(int argc, char* argv[])
{
	std::array<int, 4> ar = { 0, 1, 2, 3 };

	for(int i : ar)
	{
		std::cout << i << std::endl;
	}

	return 0;
}

[コンパイル]


clang++ test1.cpp -std=c++11 -Wall -o test1.out

test1.cpp:6:28: warning: suggest braces around initialization of subobject [-Wmissing-braces]
std::array ar = { 0, 1, 2, 3 };
^~~~~~~~~~
{ }
1 warning generated.

中括弧の数が足りないと言っています。次のコードでは警告は出ません。

#include <array>
#include <iostream>

int main(int argc, char* argv[])
{
	std::array<int, 4> ar = { { 0, 1, 2, 3 } };

	for(int i : ar)
	{
		std::cout << i << std::endl;
	}

	return 0;
}

std::arrayの実装を読むと、かなり大雑把に言えば次のようになっています。

template<typename _Tp, std::size_t _Nm>
struct array
{
    _Tp _M_elems[Nm];
    //以下メンバ関数
    //......
};

std::arrayには、publicなメンバ変数である_Tp型配列以外のメンバ変数がありません。さらにaggregateの条件を満たしているため、通常の配列と同様の初期化の仕方が可能になります。ただし、std::arrayは配列そのものではなくて配列を唯一メンバ変数に持つ構造体なので、括弧が二重に必要です。

つまり、一重括弧の方が文法的にグレーなのであって、二重括弧の方が本来正しい記述の仕方というわけです。しかしstd::arrayは_M_elemsよりも前にメンバ変数を持たないため、一重括弧でも意図した通りに動くというわけです。

結論としては、二重括弧で書いた方がいいと思います。

余談ですが、std::arrayでも生配列と同じように0初期化が可能です。

std::array<int, 4> ar = {};
std::array<int, 4> ar = { 0 };

clangでは、前者だとワーニングは出ませんが後者だと出ます。

二次元配列でも同じように初期化が可能です。{}内の要素が足りない場合、
・数値なら0で初期化
・ポインタならnullptrで初期化
・aggregateならこのルールを再帰的に適用
というルールで初期化を行うからです。次のコードも全て0で初期化できますね。

#include <array>
#include <iostream>

struct vec
{
	int x;
	int y;
};

int main(int argc, char* argv[])
{
	std::array<std::array<vec, 4>, 4> ar = {};

	for(const auto& line : ar)
	{
		for(const auto& cell : line)
		{
			std::cout << "(" << cell.x << ", " << cell.y << "), ";
		}
		std::cout << std::endl;
	}

	return 0;
}

[実行結果]
(0, 0), (0, 0), (0, 0), (0, 0),
(0, 0), (0, 0), (0, 0), (0, 0),
(0, 0), (0, 0), (0, 0), (0, 0),
(0, 0), (0, 0), (0, 0), (0, 0),