我知道已经有一些与此类似的主题(例如this)。
本主题中给出的示例是这样的:
std::string & rs1 = std::string();
显然,std::string() 是一个右值。 但是,我的问题是为什么 s1 合法而 s2 不合法?
const std::string& s1 = "String literal";
std::string& s2 = "String literal";
该标准明确规定字符串文字是左值(这是可以理解的,因为从技术上来说它们在幕后是 const char* )。 当我编译 s2 时,我得到以下信息:
prog.cpp:4:19: error: invalid initialization of non-const reference of type
'std::string& {aka std::basic_string<char>&}' from an rvalue of type
'const char*' std::string& s2 = "String literal";
我知道左值和右值的标准定义是互斥的,那么这是否是编译器潜在的错误? 我在本例中使用 gcc 4.9.2。 这是否也是文字实际上是 x 值的情况之一?
问题是字符串文字不是
std::string
类型或其子类 - 它是 char const[N]
类型。
因此,初始化程序的类型与引用的目标类型不引用兼容,并且必须创建临时对象并将其绑定到引用。
但是,临时变量不能绑定到非常量左值引用。 IE。你的情况相当于
std::string& s = std::string("Abcdefg");
即使在你看来,这也显然是不正确的。
实际上它不起作用的确切原因不是因为临时变量不能绑定到非常量左值引用,而是非常量左值引用的初始化器受到某些要求的约束,而在这种情况下
char const[N]
无法满足,[dcl.init.ref]/5:
对类型“cv1
”的引用由表达式初始化 输入“cv2T1
”,如下所示:T2
如果引用是左值引用且初始化表达式
- 是左值(但不是位字段),并且“cv1
引用兼容”与“cv2T1
”或T2
- 具有类类型(即 T2 是类类型),其中 T1 与 T2 没有引用相关,并且可以隐式转换为左值 类型为“cv3 T3”,其中“cv1
”与参考兼容 “cv3T1
”106(此转换是通过枚举适用的 转换函数(13.3.1.6)并通过以下方式选择最好的一个 重载解析(13.3)),T3
然后引用绑定到初始化表达式左值 第一种情况和转换的左值结果 第二种情况(或者在任何一种情况下,到适当的基类 对象的子对象)。
否则,引用应为对非易失性 const 类型的左值引用(即 cv1 应为 const),或者引用 应为右值引用。
- [..]
106)这需要一个返回引用类型的转换函数(12.3.2)。
字符串文字可能是左值,但它不是
string
对象。正在创建一个临时的 string
,它是一个右值。
首先,字符串是
const char *
或 const char [N]
,而不是 std::string
。所以你不是直接分配那些 char *
字符串。 std::string
有一个接受 const char [N]
的构造函数,编译器会自动使用它来构造一个新实例。
但是当对 s2 使用
const
时,编译器就无法将新的 std::string
实例分配给 s2。
所以最后我认为你误解了
const char [N]
类型的“字符串”和std::string
类型的“字符串”之间的区别。标准在谈论字符串是左值时指的是 const char [N]
,但您试图将其应用于标准规则不适用的 std::string
。
类型和值类别是两个不同的东西。
字符串文字,如“字符串文字”,其类型为 const char[13],但其值类别为 const char(&)[13]。所以你可能想知道它有名字时怎么能被认为是临时对象呢?
编译器首先需要检查要匹配的类型,直接与期望的类型不匹配。但是, std::string 有一个可以接受 const char* 的构造函数,因此首先,编译器需要创建一个临时 std::string 来进行转换。这个临时 std::string 无法绑定到 std::string& ,它需要左值引用,这就是它抛出错误的原因!