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

Tom*_*rly 71 c++ initializer-list c++11

我找到了一个不起眼的日志记录错误,事实上长度为2的初始化列表似乎是一个特例!这怎么可能?

代码是使用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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

我还要补充一点,在测试B伪造字符串的长度似乎是不确定的 - 它总是比第一初始化字符串较大,但已经比第一字符串的长度多了一个变化,以总两个字符串的长度在初始化程序中.

Fil*_*efp 76

介绍

想象一下以下声明和用法:

struct A {
  A (std::initializer_list<std::string>);
};
Run Code Online (Sandbox Code Playgroud)

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

在(A)和(C)中,每个c样式的字符串都会导致一(1)个std :: string的初始化,但正如您在问题中所述,(B)不同.

编译器看到,它可能构建的std :: string使用begin-结束迭代器,并在解析的语句(),它会更喜欢这种构造在使用"a""b"作为个体的初始化为2元.

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


:该类型的"a""b"char const[2],这样一种类型,可以隐含衰变成一个char const*,指针型,适用于像一个iterator表示无论是开始还是结束创建的std :: string .. 我们必须小心:我们导致未定义的行为,因为在调用所述构造函数时两个指针之间没有(保证)关系.


说明

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

  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
Run Code Online (Sandbox Code Playgroud)

不要使用双括号进行此类初始化.


chr*_*ris 20

首先,这是未定义的行为,除非我遗漏了一些明显的东西.现在让我解释一下.向量是从字符串的初始化列表构造的.但是,此列表仅包含一个字符串.这个字符串由内部形成{"Hello", "there"}.怎么样?使用迭代器构造函数.基本上,for (auto it = "Hello"; it != "there"; ++it)正在形成一个包含的字符串Hello\0.

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

  • 哈!它必须是未定义的行为.但是等等,为什么不是无限循环?答:因为编译器的一时兴起,这两个字符串在内存中或多或少连续布局! (3认同)