既然字符串字面值被视为lvalue,那么为什么绑定的lvalue引用必须是const呢?

16

我知道已经有类似这个话题的内容了(例如这个)。

本话题中给出的例子是:

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。这也是文字本身实际上是xvalue的一个案例吗?


因为非 const 左值引用无法绑定到临时对象,而 const 左值引用可以。从 "const char*" 创建了一个 string 类型的临时对象,然后将其绑定到引用上。 - bolov
你确定你编译的是你的示例代码吗? const std::string & s2 = "String literal" 是完全有效的,然而显然std::string & s1 = "String literal" 不是。 - mbgda
修改了它。感谢您的快速回复。我刚意识到我复制/粘贴错了。 - elau
2
“字符串字面值”是语言语法中的一个词元,而不是一个对象。由该词元表示的对象是字符数组,而不是类型为std::string的对象(这是库中的一种完全不同的东西)。 - Kerrek SB
3
这似乎有些模糊。标准规定:“尝试修改字符串文字的效果是未定义的。”,这意味着在某些情况下,字符串文字实际上就是字符数组。请注意,本句中的解释已被省略。 - Columbo
显示剩余4条评论
3个回答

22

问题在于字符串字面量不是类型为std::string或其子类的类型,而是类型为char const[N]

因此,初始化程序的类型与引用目标类型不兼容,并且必须创建临时对象并将其绑定到引用上。

但是,临时对象不能绑定到非const左值引用上。也就是说,你的情况等同于

std::string& s = std::string("Abcdefg");

根据你的说法,这是明显不合法的。实际上它不能工作的精确原因不是因为临时变量无法绑定到非常量左值引用,而是非常量左值引用的初始化器需要满足某些要求,在这种情况下char const[N]无法满足[dcl.init.ref]/5中的要求:

  

按以下方式使用类型为“cv2T2”的表达式将类型为“cv1T1”的引用初始化:

     
      
  • 如果该引用既是左值引用,且初始化表达式

         
        
    • 是左值(但不是位域),且“cv1T1”与“cv2T2”兼容或
    •   
    • 具有类类型(即T2是类类型),其中T1与T2不是引用相关,并且可以隐式转换为类型为“cv3 T3”的左值,   其中“cv1T1”与“cv3T3”兼容106(这通过枚举适用的 转换函数(13.3.1.6)并通过重载决议(13.3)选择最佳函数来选择此转换完成),
    •   
         

    则在第一种情况下,该引用绑定到初始化表达式左值,在第二种情况下, 绑定到转换的左值结果(或在任何情况下,绑定到对象的适当基类子对象)。

  •   
  • 否则,该引用必须是非易失性常量类型的左值引用(即cv1必须是const),或者引用必须是右值引用。

         
        
    • [..]
    •   
  •   
     
     

106) 这需要一个返回引用类型的转换函数(12.3.2)。


10

字符串字面量可能是一个 lvalue,但它不是一个 string 对象。会创建一个临时的 string,它是一个 rvalue。


2
进一步解释,s1 利用了这样一个规则:将 const 引用绑定到临时对象会将临时对象的生命周期延长到引用的作用域,而将非 const 引用绑定到临时对象则会导致错误。 - tmyklebu

2
首先,字符串是一个 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

1
首先,字符串字面值是 const char[N],而不是 char* - Mooing Duck
1
谢谢,但是用讽刺的口气真的必要吗? - erapert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接