8

这个问题很可能已经被问到了,但我没有找到答案。

下面的代码使用 gcc 编译,但在运行时崩溃,使用 std::length_error ( live )。

void test(const std::string &value) { std::cout << "string overload: " << value << std::endl; }

//void test(const std::vector<std::string> &) { std::cout << "vector overload" << std::endl; }

int main()
{
    test({"one", "two"});
}

从字符串的初始化列表创建字符串的能力似乎存在争议,例如,不能创建上面代码中注释掉的重载。

但即使允许这样的构造,为什么会导致失败呢?

4

2 回答 2

11

它调用

string(const char* b, const char* e) 

字符串 ctor 过载。

它仅在be指向相同的字符串文字时才有效。否则它是未定义的行为。

于 2019-08-30T10:35:11.700 回答
6

对于初学者来说,没有使用接受初始化列表的构造函数,因为这样的构造函数看起来像

basic_string(initializer_list<charT>, const Allocator& = Allocator());
                              ^^^^^

所以编译器搜索另一个合适的构造函数,它找到了这样的构造函数。它是构造函数

template<class InputIterator>
basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());

那是表达式"one""two"被认为是类型的迭代器const char *

因此该函数test具有未定义的行为。

例如,您可以编写(假设具有相同内容的字符串文字作为一个字符串文字存储在内存中,这不能保证并且取决于所选的编译器选项)。

#include <iostream>
#include <string>

void test(const std::string &value) { std::cout << "string overload: " << value << std::endl; }

//void test(const std::vector<std::string> &) { std::cout << "vector overload" << std::endl; }

int main()
{
    test({ "one", "one" + 3 });
}

你会得到一个有效的结果。

string overload: one

注意这个结构

{ "one", "two" }

不是类型的对象std::initializer_list<T>。这种结构没有类型。它是braced-init-list用作初始化器的。只需编译器首先尝试使用具有 std::initializer_list 类型的第一个参数的构造函数来与此初始化器一起使用。

例如,如果您将使用该类std::vector<const char *>,那么编译器确实会将其构造函数与 std::initializer_list 一起使用,并相应地使用此花括号初始化列表初始化其参数。例如

#include <iostream>
#include <vector>

int main()
{
    std::vector<const char *> v( { "one", "two" } );

    for ( const auto &s : v ) std::cout << s << ' ';
    std::cout << '\n';
}
于 2019-08-30T10:45:36.720 回答