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的构造函数时,有两种可能的解释:
-
外括号表示构造函数本身,内括号表示参与std :: initializer_list的元素,或者:
-
外括号表示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议阅读菲利普的优秀答案 。