为什么我可以将字符分配给字符串对象而不是字符串对象的向量?
Why can I assign characters to string objects but not to vectors of string objects?
C++ Primer 说字符和字符串文字可以转换为 string
s。
我尝试将字符分配给 string
作为:
std::string s;
s = 's';
它没有给我任何错误。
但是,当我尝试将字符分配给 string
对象的 vector
时:
std::vector<std::string> svec;
svec = {'a', 'b'};
它给了我一个错误。为什么?
第一个表达式中的字符文字赋值有效,因为 std::string
class has an overload for the assignment operator 采用 char
.
第二个表达式中的字符文字参数不能像字符串文字那样隐式转换为字符串(即 svec = {"a", "b"}
),因为 std::string
has a constructor for const char*
but not for char
:
表达式:
svec = {"a", "b"};
使用构造函数
string (const char* s);
表达式:
svec = {'a', 'b'};
无法工作,因为不存在采用单个字符参数的构造函数。
它有一个构造函数,它接受一个 initializer_list
(正如你在前面的 link 中看到的):
string (initializer_list<char> il);
因此,要使用字符文字初始化 std::string
,您需要使用大括号(即 braced initializer list):
std::vector<std::string> svec;
svec = {{'a'}, {'b'}};
如您所知,这将在向量的前 2 个位置初始化 2 个字符串,一个包含 "a"
,另一个包含 "b"
。
对于向量第一个位置的单个字符串,您可以使用:
svec = {{'a','b'}};
C++ Primer says that character and string literals may be converted to string
s.
C 风格字符串文字可以隐式转换为 std::string
,但 char
s 不能。这与赋值不同。
s = 's';
有效,因为 std::string
有一个超载的 operator=
占用 char
.
- Replaces the contents with character
ch
as if by assign(std::addressof(ch), 1)
svec = {'a', 'b'};
不起作用,因为 std::vector
仅重载了 operator=
s taking std::vector
or std::initializer_list
, both of them can't be constructed from braced-init-list {'a', 'b'}
. You might think the overload taking std::initializer_list<std::string>
could be used for this case, but char
can't be converted to std::string
implicitly (std::string
doesn't have such converting constructor 占用 char
),然后 std::initializer_list<std::string>
无法从 [= 构造25=].
作为解决方法,您可以将代码更改为
svec = {"a", "b"};
"a"
是类型 const char[2]
并且衰减 const char*
,可以隐式转换为 std::string
(通过 std::string
's converting constructor 取 const char*
),然后 std::initializer_list<std::string>
从 {"a", "b"}
构造并传递给 std::vector::operator=
。当然 svec = {std::string("a"), std::string("b")};
(或 svec = {"a"s, "b"s};
) 也可以, std::initializer_list<std::string>
将直接构造而无需隐式转换为 std::string
.
考虑以下示例:
#include <initializer_list>
#include <vector>
#include <string>
#include <type_traits>
int main() {
std::vector<std::string> svec;
// Non-ambigously deduced to std::initializer_list<char>.
auto a = {'a', 'b'};
static_assert(std::is_same_v<std::initializer_list<char>, decltype(a)>);
// (A)
// error: no match for 'operator=' (... std::vector<std::string> and 'std::initializer_list<char>')
//svec = a;
//svec = std::initializer_list<char>{'a', 'b'};
// error:
// error: unable to deduce 'std::initializer_list<auto>' from '{{'a', 'b'}}'
//auto b = {{'a', 'b'}};
// (B)
// OK.
// After all the following copy assingnments, svec.size() is 1 (a single "ab" element).
svec = {{'a', 'b'}};
svec = std::initializer_list<std::string>{{'a', 'b'}};
svec = std::initializer_list<std::string>{std::initializer_list<char>{'a', 'b'}};
svec = std::initializer_list<std::string>{"ab"};
}
(A) 处的错误消息是不言自明的:std::vector<std::string>
没有复制赋值运算符来为其分配 std::initializer_list<char>
参数. std::vector<T,Allocator>::operator=
唯一可行的重载需要类型为 std::initializer_list<T>
:
的参数
vector& operator=( std::initializer_list<T> ilist );
Replaces the contents with those identified by initializer list ilist
.
但是本例中的 T
是 std::string
,因此这种重载是不可行的。
在 (B) 中,另一方面,我们使用嵌套大括号初始化语法 {{'a', 'b'}}
这样 inner 大括号初始化列表表示一个 std::initializer_list<char>
,它可以使用 the following std::string
constructor:
basic_string( std::initializer_list<CharT> ilist,
const Allocator& alloc = Allocator() );
创建一个single std::string
temporary,它是外括号初始化列表的一部分,利用上面提到的std::vector
复制赋值运算符:
vector& operator=( std::initializer_list<T> ilist )
(B)的不同变体的结果是相同的:复制分配给svec
向量一个单向量std::string
元素.
最后,请注意,如果您要使用 "
分隔的 字符串 的花括号初始化列表复制赋值给 svec
,结果是复制分配一个包含 两个元素的向量:
svec = {"a", "b"}; // svec.size() is now 2
因为我们没有直接创建 两个元素 的 std::initializer_list<std::string>
,与上面 std::initializer_list<char>
几个 字符相比 结果是 单个字符串 。即,单个 std::string
对象也是 一个包含(零个或)多个 char
元素的容器。
理解这一点的关键是初始化列表。
首先,请注意这是行不通的:
std::string s('a');
但这确实:
std::string s{'a'};
原因是第一个需要一个接受单个 char
的构造函数,但 std::string
没有这样的构造函数。另一方面,第二个创建一个 initializer_list<char>
,std::string
确实有一个构造函数。
完全相同的推理适用于
std::vector<std::string>> vs{ 'a', 'b' };
对比
std::vector<std::string>> vs{ {'a'}, {'b'} };
第一个想要使用一个不存在的 std::string
ctor 获取 char
,第二个使用初始化列表。
至于原代码,原因是
std::string s;
s = 'a';
的工作原理是虽然 std::string
缺少接受 char
的构造函数,但它确实有一个接受 char
.
的赋值运算符
C++ Primer 说字符和字符串文字可以转换为 string
s。
我尝试将字符分配给 string
作为:
std::string s;
s = 's';
它没有给我任何错误。
但是,当我尝试将字符分配给 string
对象的 vector
时:
std::vector<std::string> svec;
svec = {'a', 'b'};
它给了我一个错误。为什么?
第一个表达式中的字符文字赋值有效,因为 std::string
class has an overload for the assignment operator 采用 char
.
第二个表达式中的字符文字参数不能像字符串文字那样隐式转换为字符串(即 svec = {"a", "b"}
),因为 std::string
has a constructor for const char*
but not for char
:
表达式:
svec = {"a", "b"};
使用构造函数
string (const char* s);
表达式:
svec = {'a', 'b'};
无法工作,因为不存在采用单个字符参数的构造函数。
它有一个构造函数,它接受一个 initializer_list
(正如你在前面的 link 中看到的):
string (initializer_list<char> il);
因此,要使用字符文字初始化 std::string
,您需要使用大括号(即 braced initializer list):
std::vector<std::string> svec;
svec = {{'a'}, {'b'}};
如您所知,这将在向量的前 2 个位置初始化 2 个字符串,一个包含 "a"
,另一个包含 "b"
。
对于向量第一个位置的单个字符串,您可以使用:
svec = {{'a','b'}};
C++ Primer says that character and string literals may be converted to
string
s.
C 风格字符串文字可以隐式转换为 std::string
,但 char
s 不能。这与赋值不同。
s = 's';
有效,因为 std::string
有一个超载的 operator=
占用 char
.
- Replaces the contents with character
ch
as if byassign(std::addressof(ch), 1)
svec = {'a', 'b'};
不起作用,因为 std::vector
仅重载了 operator=
s taking std::vector
or std::initializer_list
, both of them can't be constructed from braced-init-list {'a', 'b'}
. You might think the overload taking std::initializer_list<std::string>
could be used for this case, but char
can't be converted to std::string
implicitly (std::string
doesn't have such converting constructor 占用 char
),然后 std::initializer_list<std::string>
无法从 [= 构造25=].
作为解决方法,您可以将代码更改为
svec = {"a", "b"};
"a"
是类型 const char[2]
并且衰减 const char*
,可以隐式转换为 std::string
(通过 std::string
's converting constructor 取 const char*
),然后 std::initializer_list<std::string>
从 {"a", "b"}
构造并传递给 std::vector::operator=
。当然 svec = {std::string("a"), std::string("b")};
(或 svec = {"a"s, "b"s};
) 也可以, std::initializer_list<std::string>
将直接构造而无需隐式转换为 std::string
.
考虑以下示例:
#include <initializer_list>
#include <vector>
#include <string>
#include <type_traits>
int main() {
std::vector<std::string> svec;
// Non-ambigously deduced to std::initializer_list<char>.
auto a = {'a', 'b'};
static_assert(std::is_same_v<std::initializer_list<char>, decltype(a)>);
// (A)
// error: no match for 'operator=' (... std::vector<std::string> and 'std::initializer_list<char>')
//svec = a;
//svec = std::initializer_list<char>{'a', 'b'};
// error:
// error: unable to deduce 'std::initializer_list<auto>' from '{{'a', 'b'}}'
//auto b = {{'a', 'b'}};
// (B)
// OK.
// After all the following copy assingnments, svec.size() is 1 (a single "ab" element).
svec = {{'a', 'b'}};
svec = std::initializer_list<std::string>{{'a', 'b'}};
svec = std::initializer_list<std::string>{std::initializer_list<char>{'a', 'b'}};
svec = std::initializer_list<std::string>{"ab"};
}
(A) 处的错误消息是不言自明的:std::vector<std::string>
没有复制赋值运算符来为其分配 std::initializer_list<char>
参数. std::vector<T,Allocator>::operator=
唯一可行的重载需要类型为 std::initializer_list<T>
:
vector& operator=( std::initializer_list<T> ilist );
Replaces the contents with those identified by initializer list
ilist
.
但是本例中的 T
是 std::string
,因此这种重载是不可行的。
在 (B) 中,另一方面,我们使用嵌套大括号初始化语法 {{'a', 'b'}}
这样 inner 大括号初始化列表表示一个 std::initializer_list<char>
,它可以使用 the following std::string
constructor:
basic_string( std::initializer_list<CharT> ilist,
const Allocator& alloc = Allocator() );
创建一个single std::string
temporary,它是外括号初始化列表的一部分,利用上面提到的std::vector
复制赋值运算符:
vector& operator=( std::initializer_list<T> ilist )
(B)的不同变体的结果是相同的:复制分配给svec
向量一个单向量std::string
元素.
最后,请注意,如果您要使用 "
分隔的 字符串 的花括号初始化列表复制赋值给 svec
,结果是复制分配一个包含 两个元素的向量:
svec = {"a", "b"}; // svec.size() is now 2
因为我们没有直接创建 两个元素 的 std::initializer_list<std::string>
,与上面 std::initializer_list<char>
几个 字符相比 结果是 单个字符串 。即,单个 std::string
对象也是 一个包含(零个或)多个 char
元素的容器。
理解这一点的关键是初始化列表。
首先,请注意这是行不通的:
std::string s('a');
但这确实:
std::string s{'a'};
原因是第一个需要一个接受单个 char
的构造函数,但 std::string
没有这样的构造函数。另一方面,第二个创建一个 initializer_list<char>
,std::string
确实有一个构造函数。
完全相同的推理适用于
std::vector<std::string>> vs{ 'a', 'b' };
对比
std::vector<std::string>> vs{ {'a'}, {'b'} };
第一个想要使用一个不存在的 std::string
ctor 获取 char
,第二个使用初始化列表。
至于原代码,原因是
std::string s;
s = 'a';
的工作原理是虽然 std::string
缺少接受 char
的构造函数,但它确实有一个接受 char
.