yohhoyの日記

技術的メモをしていきたい日記

initializer_listとMove-only型

C++11で導入されたリスト初期化(list initialization)構文で用いられるstd::initializer_list<E>では、要素型Eにコピー操作を要求するため*1std::unique_ptrなどのコピー不可/ムーブのみ可能な(Move-only)型を扱えない。この制限により Move-only 型の標準コンテナ初期化ではリスト初期化を利用できず、メンバ関数push_backemplace_backで1要素づつ追加する必要がある。

本記事の内容はStack Overflowで見つけた質問と回答に基づく。

std::vector<std::unique_ptr<int>> vec0 = {
  std::make_unique<int>(1), std::make_unique<int>(2), std::make_unique<int>(3)
};  // NG: ill-formed
    // コピーコンストラクタunique_ptr(const unique_ptr&)が選択されるが
    // Move-only型では同オーバーロードはdelete宣言されている。

std::vector<std::unique_ptr<int>> vec1;
vec.push_back(std::make_unique<int>(1));  // OK
vec.push_back(std::make_unique<int>(2));  // OK
vec.emplace_back(new int(3));  // OKだが... unique_ptrには例外安全性の観点から非推奨

C++11 8.5.4/p5-6より引用(下線部は強調)。

5 An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array. If a narrowing conversion is required to initialize any of the elements, the program is ill-formed. [Example:

struct X {
  X(std::initializer_list<double> v);
};
X x{ 1,2,3 };

The initialization will be implemented in a way roughly equivalent to this:

double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));

assuming that the implementation can construct an initializer_list object with a pair of pointers. -- end example]
6 The lifetime of the array is the same as that of the initializer_list object. [Example:

typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };

void f() {
  std::vector<cmplx> v2{ 1, 2, 3 };
  std::initializer_list<int> i3 = { 1, 2, 3 };
}

For v1 and v2, the initializer_list object and array created for { 1, 2, 3 } have full-expression lifetime. For i3, the initializer_list object and array have automatic lifetime. -- end example] [Note: The implementation is free to allocate the array in read-only memory if an explicit array with the same initializer could be so allocated. -- end note]

関連URL

*1:同クラステンプレートが提供するイテレータ(initalizer_list<E>::iterator)は、要素Eへのconstポインタ型(const E*)となっている。通常のムーブコンストラクタ/代入演算子は非const右辺値参照型(E&&)を要求するため、initializer_list から要素をムーブで取り出すことができない。