読者です 読者をやめる 読者になる 読者になる

Uniform initialization で困るパターン(std::string)

C++

C++11 で,() でなく {} で初期化する統一初期化(Uniform initialization)が入った.普段はこれを使っているけれど,この間これで std::string でハマったのでメモ.

std::stringコンストラクタの中には,char 型の値を一定回数繰り返すコンストラクタがある.

basic_string( size_type count,
              CharT ch,
              const Allocator& alloc = Allocator() );

これを使おうとして std::string{3, 'a'} などと書こうとすると,narrow conversion でコンパイラに怒られる.原因は,統一初期化だと次のコンストラクタが優先して使われてしまうからっぽい.

basic_string( std::initializer_list<CharT> init,
              const Allocator& alloc = Allocator() );

このコンストラクタは,例えば {'a', 'b', 'c'} を渡すと "abc" に初期化してくれるというものだけれど,僕はこれの存在を知らなかったためハマってしまった.もちろん,std::string(3, 'a') と以前の書き方を使うとこの問題は起こらない.

今ところ,C++03 の方法で初期化するしかないかなぁと思っているけれど,「こうすれば良いよ」というのがあったら教えて下さい.

追記(2014/12/17)

Effective Modern C++ にまったく同じ問題が載っていた.std::initializer_list を引数に取るコンストラクタがある場合,(たとえ他に引数がベストマッチするコンストラクタがあっても)std::initializer_list を引数に取るコンストラクタが選ばれる.しかし,ここでは int から char への narrow conversion が必要であり,uniformed initialization では暗黙の narrow conversion は禁止されているのでエラーになる.結果として,よりマッチするコンストラクタがあるにも関わらず std::initializer_list を取るコンストラクタ内でエラーになる.