C ++ 11初始化程序列表失败 – 但仅限于长度为2的列表

我跟踪了一个模糊的日志logging错误,因为长度为2的初始化列表似乎是一个特例! 这怎么可能?

使用CXXFLAGS=-std=c++11 -stdlib=libc++ ,使用Apple LLVM版本5.1(clang-503.0.40) CXXFLAGS=-std=c++11 -stdlib=libc++

 #include <stdio.h> #include <string> #include <vector> using namespace std; typedef vector<string> Strings; void print(string const& s) { printf(s.c_str()); printf("\n"); } void print(Strings const& ss, string const& name) { print("Test " + name); print("Number of strings: " + to_string(ss.size())); for (auto& s: ss) { auto t = "length = " + to_string(s.size()) + ": " + s; print(t); } print("\n"); } void test() { Strings a{{"hello"}}; print(a, "a"); Strings b{{"hello", "there"}}; print(b, "b"); Strings c{{"hello", "there", "kids"}}; print(c, "c"); Strings A{"hello"}; print(A, "A"); Strings B{"hello", "there"}; print(B, "B"); Strings C{"hello", "there", "kids"}; print(C, "C"); } int main() { test(); } 

输出:

 Test a Number of strings: 1 length = 5: hello Test b Number of strings: 1 length = 8: hello Test c Number of strings: 3 length = 5: hello length = 5: there length = 4: kids Test A Number of strings: 1 length = 5: hello Test B Number of strings: 2 length = 5: hello length = 5: there Test C Number of strings: 3 length = 5: hello length = 5: there length = 4: kids 

我还应该补充一点,在testingb中伪造string的长度似乎是不确定的 – 它总是大于第一个初始化string,但是从第一个string的长度到两个string的总长度在初始化器中。

介绍

想象下面的声明和用法:

 struct A { A (std::initializer_list<std::string>); }; 

 A {{"a" }}; // (A), initialization of 1 string A {{"a", "b" }}; // (B), initialization of 1 string << !! A {{"a", "b", "c"}}; // (C), initialization of 3 strings 

在( A )和( C )中,每个c样式的string引起一个(1) std :: string的初始化,但正如你在问题中所说的那样,( B )是不同的。

编译器认为有可能使用开始和结束迭代器来构造一个std :: string ,并且在parsing语句( B )时,它将优先使用"a""b"作为两个元素的单独初始值设定项。

 A { std::string { "a", "b" } }; // the compiler's interpretation of (B) 

注意"a""b"char const[2] ,它可以隐式地衰减到char const* ,这是一个适合作为迭代器的指针types,表示创build时的开始结束 一个std :: string .. 但是我们必须小心:因为调用构造函数时两个指针之间没有(保证的)关系,所以我们导致了未定义的行为


说明

当你调用一个使用双花括号{{ a, b, ... }}std :: initializer_list的构造函数时,有两种可能的解释:

  1. 外括号表示构造函数本身,内括号表示参与std :: initializer_list的元素,或者:

  2. 外括号表示std :: initializer_list ,而内括号表示内部元素的初始化。

这是最好的做2),只要这是可能的,因为std::string有一个构造函数采取两个迭代器,它是当你有std::vector<std::string> {{ "hello", "there" }}

另外的例子:

 std::vector<std::string> {{"this", "is"}, {"stackoverflow"}}.size (); // yields 2 

不要使用双括号进行这种初始化。

首先,这是不明确的行为,除非我失去了明显的东西。 现在让我解释一下。 该向量是从一个string的初始化列表构造的。 但是这个列表只包含一个string。 这个string由内部{"Hello", "there"} 。 怎么样? 用迭代器的构造函数。 本质上, for (auto it = "Hello"; it != "there"; ++it)正在形成一个包含Hello\0的string。

举个简单的例子, 看这里 。 虽然UB是足够理性的,但似乎第二个字面正好放在记忆中的第一个字符之后。 作为奖励,做"Hello", "Hello" ,你可能会得到一串长度为0.如果你在这里什么都不明白,我build议阅读菲利普的优秀答案 。