简体   繁体   English

用右值初始化左值引用

[英]Initializing lvalue reference with rvalue

I built this code with gcc/clang and got different results: 我用gcc / clang构建了此代码,并得到了不同的结果:

#include <iostream>
#include <sstream>

int main() {
    std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
}
  • Why gcc allows initializing lvalue reference with rvalue ( std::stringstream("") )? 为什么gcc允许使用rvalue( std::stringstream("") )初始化左值引用?
  • Why clang tries to call copy constructor? 为什么clang尝试调用复制构造函数?

gcc 4.9.1 gcc 4.9.1

No error 没错

clang 3.4 lang声3.4

prog.cc:5:63: error: call to implicitly-deleted copy constructor of 'istream' (aka 'basic_istream<char>')
    std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
                                                             ^~~~~~~~
/usr/local/libcxx-3.4/include/c++/v1/istream:185:5: note: copy constructor is implicitly deleted because 'basic_istream<char, std::__1::char_traits<char> >' has a user-declared move constructor
   basic_istream(basic_istream&& __rhs);
   ^
prog.cc:5:28: error: calling a protected constructor of class 'std::__1::basic_istream<char, std::__1::char_traits<char> >'
   std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
                          ^
/usr/local/libcxx-3.4/include/c++/v1/istream:185:5: note: declared protected here
   basic_istream(basic_istream&& __rhs);
   ^
prog.cc:5:28: error: calling a protected constructor of class 'std::__1::basic_istream<char, std::__1::char_traits<char> >'
   std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
                          ^
/usr/local/libcxx-3.4/include/c++/v1/istream:185:5: note: declared protected here
   basic_istream(basic_istream&& __rhs);
   ^

GCC's behavior is a bug, and it's been fixed on trunk . GCC的行为是一个错误,并且已在trunk上修复 Clang is correct. lang是正确的。 This is a messy case because you have mixed value categories for the second and third operands of the conditional operator: 这是一个混乱的情况,因为您为条件运算符的第二和第三操作数混合了值类别:

  • std::move(std::stringstream("")) is an xvalue * of type std::stringstream ; std::move(std::stringstream(""))是类型为std::stringstream的xvalue *
  • std::cin is an lvalue of type std::istream . std::cinstd::istream类型的左值。

The relevant standard quote (§5.16 [expr.cond]/p3-6) can be found in this answer . 相关标准报价(§5.16[expr.cond] / p3-6)可以在此答案中找到。 It's long enough that I don't really want to copy it over. 足够长的时间,我真的不想复制它。 I'll just outline how it is applied to this code: 我将概述它如何应用于此代码:

  • Obviously std::istream cannot be converted to match std::stringstream in any way regardless of value category; 显然,无论值类别如何,都无法以任何方式将std::istream转换为与std::stringstream匹配;
  • An xvalue of type std::stringstream cannot be converted to type "lvalue reference to std::istream " given the constraint that the reference must bind directly to an lvalue - there's no lvalue here for the reference to bind to; 考虑到引用必须直接绑定到lvalue的约束,不能将类型std::stringstream的xvalue转换为“对std::istream lvalue引用”类型-这里没有lvalue可供引用绑定;
  • std::istream is a base class of std::stringstream , so per the 3rd bullet of p3, the xvalue of type std::stringstream can and will be converted to a prvalue temporary of type std::istream by copy-initialization, which replaces the original operand for further analysis. std::istreamstd::stringstream的基类,因此根据p3的第3个项目符号,可以并且通过复制初始化将类型std::stringstream的xvalue转换为类型为std::istream的prvalue临时对象,它将替换原始操作数以进行进一步分析。
  • Now the second operand is a prvalue of type std::istream , the third operand is an lvalue of type std::istream , they have different value categories so p4 doesn't apply. 现在,第二个操作数是std::istream类型的prvalue,第三个操作数是std::istream类型的lvalue,它们具有不同的值类别,因此p4不适用。
  • Hence the result is a prvalue per p5. 因此,结果是每p5的prvalue。 Since they have the same type, overload resolution specified in p5 is not performed, and you proceed to p6. 由于它们具有相同的类型,因此不会执行p5中指定的重载分辨率,然后进入p6。
  • The applicable bullet in p6 is 第6页中的适用项目符号为

    The second and third operands have the same type; 第二和第三操作数具有相同的类型。 the result is of that type. 结果就是这种类型。 If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand. 如果操作数具有类类型,则结果是该结果类型的prvalue临时值,它会根据第一个操作数的值从第二个操作数或第三个操作数进行复制初始化。

    so it copy-initializes the result (which is a prvalue temporary) from either the converted first operand, or the second operand ( std::cin ). 因此它从转换后的第一个操作数或第二个操作数( std::cin )复制结果(这是一个prvalue临时值)。

Hence the errors: 因此错误:

  • Copy-initializing the prvalue std::istream result from an lvalue ( std::cin ) would use the copy constructor, and streams cannot be copied. 从左值( std::cin )复制初始化prvalue std::istream结果将使用复制构造函数,并且无法复制流。
  • Copy-initializing the prvalue temporary std::istream for the second operand from a std::stringstream xvalue is a move, but std::istream 's move constructor is protected. std::stringstream xvalue复制第二个操作数的prvalue临时std::istream是移动,但std::istream的move构造函数受到保护。

* For terminology (lvalue, xvalue, prvalue, etc.), see What are rvalues, lvalues, xvalues, glvalues, and prvalues? *有关术语(lvalue,xvalue,prvalue等),请参阅什么是rvalue,lvalue,xvalues,glvalues和prvalue?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM