使用rvalue初始化左值引用

时间:2015-01-10 18:29:21

标签: c++ c++11 gcc clang

我用gcc / clang构建了这段代码并得到了不同的结果:

#include <iostream>
#include <sstream>

int main() {
    std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
}
  • 为什么gcc允许使用rvalue(std::stringstream(""))初始化左值引用?
  • 为什么clang会尝试调用复制构造函数?

gcc 4.9.1

没有错误

clang 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);
   ^

1 个答案:

答案 0 :(得分:6)

GCC的行为是一个错误,它是been fixed on trunk。铿锵是对的。这是一个混乱的情况,因为你有条件运算符的第二个和第三个操作数的混合值类别:

  • std::move(std::stringstream(""))std::stringstream类型的xvalue * ;
  • std::cinstd::istream类型的左值。

相关标准引用(§5.16[expr.cond] / p3-6)可在this answer中找到。它足够长,我真的不想复制它。我将概述它如何应用于此代码:

  • 显然std::istream无论价值类别如何都无法以任何方式转换为匹配std::stringstream;
  • 类型std::stringstream的xvalue无法转换为类型“std::istream的左值引用”,前提条件是引用必须直接绑定到左值 - 这里没有左值来引用绑定到;
  • std::istreamstd::stringstream的基类,因此根据p3的第3个子弹,类型std::stringstream的xvalue可以并且将被转换为{{1}类型的prvalue临时值通过复制初始化,它替换原始操作数以供进一步分析。
  • 现在第二个操作数是std::istream类型的prvalue,第三个操作数是std::istream类型的左值,它们具有不同的值类别,因此p4不适用。
  • 因此结果是每p5的prvalue。由于它们具有相同的类型,因此不会执行p5中指定的重载分辨率,并继续执行p6。
  • p6中的适用项目是

      

    第二个和第三个操作数具有相同的类型;结果是   那种类型。如果操作数具有类类型,则结果为prvalue   结果类型的临时,从任一个复制初始化   第二个操作数或第三个操作数取决于的值   第一个操作数。

    所以它从转换后的第一个操作数或第二个操作数(std::istream)复制初始化结果(这是一个prvalue临时值)。

因此错误:

  • 从左值(std::cin)复制初始化prvalue std::istream结果将使用复制构造函数,并且无法复制流。
  • std::cin xvalue复制初始化第二个操作数的prvalue临时std::istream是一个移动,但std::stringstream的移动构造函数受到保护。

* 对于术语(左值,左值,右值等),请参阅What are rvalues, lvalues, xvalues, glvalues, and prvalues?