C++標準ライブラリの文字列型std::string
と、リスト初期化(list initialization)の組み合わせによる落とし穴。
2個の文字列リテラルによるstd::string
のリスト初期化は、ほとんどのC++処理系においてコンパイル時には問題検知されないが*1、実行時に未定義動作(undefined behavior)を引き起こす。
#include <string> std::string s1 = { "abc" }; // OK: s1 == "abc" std::string s2 = { "abc", "xyz" }; // NG: 未定義動作
std::string
の厳密な型はstd::basic_string<char, std::char_traits<char>, std::allocator<charT>>
となる。簡単のためstd::allocator<charT>>
を単にA
と略記する。
s1
ではコンストラクタbasic_string(const char*, const A& = A())
が選択される。第1引数に渡されるポインタ値は有効なヌル終端文字列を指すためwell-defined。s2
では文字列リテラルからポインタ型const char*
へ暗黙変換され(2.14.5/p8, 4.2/p1)、テンプレートコンストラクタtemplate<class II> basic_string(II begin, II end, const A& = A())
が選択される(II
=const char*
)。第1, 2引数に渡されるポインタ値の比較演算は未規定(unspecified)であり(5.9/p2)*2、半開区間 [begin, end) は有効な区間となる保証がない。これは標準ライブラリ要件に違反するため、プログラムは未定義動作を引き起こす。
C++11 2.14.5/p12, 21.4.2/p14-15, 23.2.3/p3より一部引用。
Whether all string literals are distinct (that is, are stored in nonoverlapping objects) is implementation-defined. (snip)
template<class InputIterator> basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());Effects: If
InputIterator
is an integral type, equivalent tobasic_string(static_cast<size_type>(begin), static_cast<value_type>(end), a)
Otherwise constructs a string from the values in the range [begin
,end
), as indicated in the Sequence Requirements table (see 23.2.3).
In Tables 100 and 101,
X
denotes a sequence container class, (snip),i
andj
denote iterators satisfying input iterator requirements and refer to elements implicitly convertible tovalue_type
,[i, j)
denotes a valid range, (snip)
関連URL