std::initializer_list std::string 的奇怪行为

Strange behavior of std::initializer_list of std::strings

本文关键字:std string initializer list      更新时间:2023-10-16

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

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

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"});
}

从字符串的初始值设定项列表创建字符串的功能似乎存在争议,例如,无法创建上面代码中注释掉的重载。

但即使允许这样的施工,为什么会导致失败?

它调用

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

字符串 ctor 重载。

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

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

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 一起使用,并相应地使用此大括号的 init-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';
}