std::strings 的 std::initializer_list 的奇怪行为
Strange behavior of std::initializer_list of std::strings
这个问题很可能已经有人问过了,但我没有找到答案。
下面的代码使用 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"});
}
从字符串的初始值设定项列表创建字符串的能力似乎存在争议,例如,无法创建上面代码中注释掉的重载。
但是即使允许这样的构造,为什么会导致失败呢?
它调用
string(const char* b, const char* e)
字符串构造函数重载。
仅当 b
和 e
指向相同的字符串文字时才有效。否则它是未定义的行为。
对于初学者来说,没有使用接受初始化列表的构造函数,因为这样的构造函数看起来像
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
具有未定义的行为。
你可以这样写(假设具有相同内容的字符串文字作为一个字符串文字存储在内存中,which is not guaranteed并且取决于所选的编译器选项)。
#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 的第一个参数的构造函数来与此初始化程序一起使用。
例如,如果您将使用 class 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';
}
这个问题很可能已经有人问过了,但我没有找到答案。
下面的代码使用 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"});
}
从字符串的初始值设定项列表创建字符串的能力似乎存在争议,例如,无法创建上面代码中注释掉的重载。
但是即使允许这样的构造,为什么会导致失败呢?
它调用
string(const char* b, const char* e)
字符串构造函数重载。
仅当 b
和 e
指向相同的字符串文字时才有效。否则它是未定义的行为。
对于初学者来说,没有使用接受初始化列表的构造函数,因为这样的构造函数看起来像
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
具有未定义的行为。
你可以这样写(假设具有相同内容的字符串文字作为一个字符串文字存储在内存中,which is not guaranteed并且取决于所选的编译器选项)。
#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 的第一个参数的构造函数来与此初始化程序一起使用。
例如,如果您将使用 class 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';
}