#include <iostream>
using namespace std;
int main()
{
std::ostream o(nullptr);
true ? std::ostream(nullptr) : std::ostream(nullptr); // A
true ? std::ostream(nullptr) : o; //B
return 0;
}
我想知道为什么
A
可以正常编译,而B
失败并显示错误:prog.cpp: In function ‘int main()’:
prog.cpp:7:33: error: ‘std::basic_ostream<_CharT, _Traits>::basic_ostream(const std::basic_ostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ is protected within this context
true ? o : std::ostream(nullptr);
^
因此,我找到了这个站点:https://docs.microsoft.com/en-us/cpp/cpp/conditional-operator-q?view=vs-2017,它表示当三元运算符具有参数时(通过“参数”,我指的是
:
左右两边的参数),则可以导致参数的复制,强制转换等。 std::ostream
具有定义为protected
的副本构造函数。因此,在A
中,三元运算符将获得相同类型的两个参数,因此不会进行复制,因此没有问题。而在B
中,三元运算符会获得不同类型的参数,这显然会导致需要std::ostream
进行复制。到目前为止一切似乎还可以。但是后来我尝试了这个:
#include <iostream>
using namespace std;
int main()
{
std::ostream o(nullptr);
std::ostream & oRef = o;
std::ostream && oRRef = std::ostream(nullptr);
true ? std::ostream(nullptr) : std::ostream(nullptr); // A
true ? std::ostream(nullptr) : o; //B
true ? std::ostream(nullptr) : oRef; // C
true ? std::ostream(nullptr) : oRRef; // D
true ? std::ostream(nullptr) : std::move(oRRef); // E
return 0;
}
C
,D
和E
也会因类似错误而失败。所以我有几个问题。表达式的类型是什么:
std::ostream(nullptr)
?三元运算符在具有不同类型的情况下会复制其参数,而在具有相同类型的情况下却从不复制吗?我想念或需要知道的其他内容吗? 最佳答案
该标准包含一些有关如何评估条件表达式的复杂规则([expr.cond])。但是,除了在这里引用这些规则之外,我将解释您应该如何考虑它们。
条件表达式的结果可以是lvalue,xvalue或prvalue。但是必须在编译时知道其中之一。 (表达式的值类别永远不能取决于运行时发生的事情)。很容易看出,如果第二个和第三个表达式都是相同类型的左值,那么结果也可以设为左值,而不必进行复制。如果第二个和第三个表达式都是相同类型的prvalue,则从C++ 17开始,也不会发生复制--T
类型的prvalue表示T
类型的对象的延迟初始化,并且编译器仅根据条件选择要传递的那两个prvalue中的哪个,最终用于初始化对象。
但是,当一个表达式是左值,而另一个表达式是相同类型的prvalue时,则结果必须是prvalue。如果标准说结果将是左值,那将是不合逻辑的,因为条件可能导致选择prvalue操作数,并且您无法将prvalue转换为左值。但是您可以反过来做。因此,该标准表示,当一个操作数是左值,而另一个是相同类型的prvalue时,则左值必须经过左值到右值的转换。并且,如果您尝试对std::ostream
对象进行从左值到右值的转换,则由于复制构造函数被删除,程序将格式错误。
从而:
o
需要从左值到右值转换,因此不会编译。 oRef
是一个左值,因此仍然需要从左值到右值的转换,因此也不会编译。 oRRef
仍然是左值(因为右值引用的名称是左值)。 E的情况值得进一步说明。在C++ 11中,很明显,如果一个参数是xvalue,而另一个参数是相同类型的prvalue,则xvalue必须经过(误称)从lvalue到rvalue的转换才能产生prvalue。在
std::ostream
的情况下,它使用 protected move构造函数(因此该程序违反了成员访问控制)。在C++ 17中,可以考虑更改规则,以便不再将xvalue转换为prvalue,而是将prvalue实体化以产生xvalue,从而避免了移动的需要。但是,这种更改没有明显的好处,并且是否是最合理的行为值得怀疑,因此这可能就是为什么不进行更改的原因(如果考虑的话)。关于c++ - 为什么在三元运算符中使用std::ostream不能编译?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54714735/