用右值初始化左值引用
Initializing lvalue reference with rvalue
我用 gcc/clang 构建了这段代码并得到了不同的结果:
#include <iostream>
#include <sstream>
int main() {
std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
}
- 为什么 gcc 允许使用右值 (
std::stringstream("")
) 初始化左值引用?
- 为什么 clang 会尝试调用复制构造函数?
海湾合作委员会 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);
^
GCC 的行为是一个错误,它是 been fixed on trunk。铛是正确的。这是一个混乱的情况,因为条件运算符的第二个和第三个操作数具有混合值类别:
std::move(std::stringstream(""))
是 std::stringstream
; 类型的 xvalue*
std::cin
是 std::istream
. 类型的左值
相关标准引用(§5.16 [expr.cond]/p3-6)可在this answer中找到。够长了,我真的不想复制过来。我将概述它是如何应用于此代码的:
- 显然
std::istream
无法以任何方式转换为与 std::stringstream
匹配,无论值类别如何;
- 鉴于引用必须直接绑定到左值的约束,
std::stringstream
类型的 xvalue 无法转换为类型 "lvalue reference to std::istream
" - 这里没有左值供引用绑定;
std::istream
是 std::stringstream
的基数 class,因此根据 p3 的第 3 个项目符号,类型 std::stringstream
的 xvalue 可以并且将会转换为 prvalue通过复制初始化临时 std::istream
类型,它替换原始操作数以供进一步分析。
- 现在第二个操作数是
std::istream
类型的纯右值,第三个操作数是std::istream
类型的左值,它们有不同的值类别,所以p4不适用。
- 因此结果是每个 p5 的纯右值。由于它们具有相同的类型,因此不执行 p5 中指定的重载决议,然后继续进行 p6。
p6中适用的项目符号是
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.
因此它从转换后的第一个操作数或第二个操作数 (std::cin
) 复制初始化结果(临时纯右值)。
因此出现错误:
- 从左值 (
std::cin
) 复制初始化纯右值 std::istream
结果将使用复制构造函数,并且无法复制流。
- 从
std::stringstream
xvalue 为第二个操作数复制初始化 prvalue temporary std::istream
是一个移动,但 std::istream
的移动构造函数受到保护。
* 有关术语(左值、xvalue、prvalue 等),请参阅 What are rvalues, lvalues, xvalues, glvalues, and prvalues?
我用 gcc/clang 构建了这段代码并得到了不同的结果:
#include <iostream>
#include <sstream>
int main() {
std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
}
- 为什么 gcc 允许使用右值 (
std::stringstream("")
) 初始化左值引用? - 为什么 clang 会尝试调用复制构造函数?
海湾合作委员会 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);
^
GCC 的行为是一个错误,它是 been fixed on trunk。铛是正确的。这是一个混乱的情况,因为条件运算符的第二个和第三个操作数具有混合值类别:
std::move(std::stringstream(""))
是std::stringstream
; 类型的 xvalue*
std::cin
是std::istream
. 类型的左值
相关标准引用(§5.16 [expr.cond]/p3-6)可在this answer中找到。够长了,我真的不想复制过来。我将概述它是如何应用于此代码的:
- 显然
std::istream
无法以任何方式转换为与std::stringstream
匹配,无论值类别如何; - 鉴于引用必须直接绑定到左值的约束,
std::stringstream
类型的 xvalue 无法转换为类型 "lvalue reference tostd::istream
" - 这里没有左值供引用绑定; std::istream
是std::stringstream
的基数 class,因此根据 p3 的第 3 个项目符号,类型std::stringstream
的 xvalue 可以并且将会转换为 prvalue通过复制初始化临时std::istream
类型,它替换原始操作数以供进一步分析。- 现在第二个操作数是
std::istream
类型的纯右值,第三个操作数是std::istream
类型的左值,它们有不同的值类别,所以p4不适用。 - 因此结果是每个 p5 的纯右值。由于它们具有相同的类型,因此不执行 p5 中指定的重载决议,然后继续进行 p6。
p6中适用的项目符号是
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.
因此它从转换后的第一个操作数或第二个操作数 (
std::cin
) 复制初始化结果(临时纯右值)。
因此出现错误:
- 从左值 (
std::cin
) 复制初始化纯右值std::istream
结果将使用复制构造函数,并且无法复制流。 - 从
std::stringstream
xvalue 为第二个操作数复制初始化 prvalue temporarystd::istream
是一个移动,但std::istream
的移动构造函数受到保护。
* 有关术语(左值、xvalue、prvalue 等),请参阅 What are rvalues, lvalues, xvalues, glvalues, and prvalues?