为什么具有两个元素的初始化语法将一个元素放入字符串向量而不是两个?

Why does initalizer syntax with two elements put one element to a vector of strings instead of two?

为什么我在 b 中得到一个元素而不是两个?在 a 中,我得到了一个预期的元素,在 c 中,我得到了三个预期的元素。具有两个值的那个在某种程度上是一种特殊情况。

#include <string>
#include <vector>
#include <iostream>

void print(const std::string& name, const std::vector<std::string>& v)
{
    std::cout << name << ' ' << v.size() << '\n';
    for (const auto& str : v) {
        std::cout << str << '\n';
    }
    std::cout << "\n";
}

int main()
{
    std::vector<std::string> a = {{"str1"}};
    std::vector<std::string> b = {{"str1", "str2"}};
    std::vector<std::string> c = {{"str1", "str2", "str3"}};
    print("a", a);
    print("b", b);
    print("c", c);
    return 0;
}

这会打印:

a 1
str1

b 1
str1

c 3
str1
str2
str3

我想这与向量 ctor 的重载有关。

template< class InputIt >
vector( InputIt first, InputIt last,
    const Allocator& alloc = Allocator() );

我使用 clang 9.0.0 和 -std=c++17 -O2 -Wall 作为标志。

编译器在b的情况下做了什么?为什么它在一种情况下决定它是迭代器而在其他情况下决定它是初始化列表?我的示例代码是否定义明确或有 UB?

Is my sample code well defined or does it have UB?

确实有UB。采用 first-last 迭代器对的构造函数假定两个迭代器引用相同的序列。 {{"str1"}, {"str2"}} 不满足此要求,因此 UB。

What did the compiler do in the case of b? Why did it decide it's an iterator in one case and initializer list in the other cases?

回想一下,字符串文字 "str1" 的类型是 char const[5]。但是,对 std::initializer_list 的构造函数重载调用适用于 std::string 实例。这需要隐式转换 - 由于两个迭代器的构造函数模板不需要这样的转换,因此它被认为是更好的匹配。


您可以通过使用一对大括号强制直接列表初始化来解决这个问题,

std::vector<std::string> b2 = {"str1", "str2"};

或至少手动指定一次所需的类型:

std::vector<std::string> b3 = {{std::string("str1"), "str2"}};

using namespace std::string_literals;
std::vector<std::string> b4 = {{"str1"s, "str2"s}};