【问题标题】:Initializing lvalue reference with rvalue用右值初始化左值引用
【发布时间】:2015-03-08 21:41:38
【问题描述】:

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

#include <iostream>
#include <sstream>

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

gcc 4.9.1

没有错误

叮当声 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);
   ^

【问题讨论】:

  • 我知道clang在做什么,而且是正确的。我不知道 GCC 在做什么。
  • 似乎是混淆 GCC 的 std::move。没有它,它将给出适当的错误。这很奇怪,因为 std::move 在这里实际上是多余的。

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


【解决方案1】:

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 可以并且将通过复制初始化转换为 std::istream 类型的临时纯右值,它替换原来的操作数以供进一步分析。
  • 现在第二个操作数是 std::istream 类型的纯右值,第三个操作数是 std::istream 类型的左值,它们具有不同的值类别,因此 p4 不适用。
  • 因此结果是每个 p5 的纯右值。由于它们具有相同的类型,因此不执行 p5 中指定的重载决议,并继续执行 p6。
  • p6 中适用的项目符号是

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

    因此它从转换后的第一个操作数或第二个操作数 (std::cin) 复制初始化结果(这是一个临时纯右值)。

因此出现错误:

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

* 有关术语(左值、xvalue、prvalue 等),请参阅What are rvalues, lvalues, xvalues, glvalues, and prvalues?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多