C++ std::可选:简单值和ref限定值之间的有效差异()
这是一个学术问题。C++ std::可选:简单值和ref限定值之间的有效差异(),c++,c++17,stdoptional,C++,C++17,Stdoptional,这是一个学术问题。std::optional类型有一个T&&value()&&方法。我有以下定义: class A { ... }; void f(A &&a); 以及以下节目: std::optional<A> optA; optA = A(1337); f(std::move(optA).value()); // OPTION 1 f(std::move(optA.value())); // OPTION 2 std::cout << optA.ha
std::optional
类型有一个T&&value()&&
方法。我有以下定义:
class A { ... };
void f(A &&a);
以及以下节目:
std::optional<A> optA;
optA = A(1337);
f(std::move(optA).value()); // OPTION 1
f(std::move(optA.value())); // OPTION 2
std::cout << optA.has_value() << std::endl;
std::可选optA;
optA=A(1337);
f(std::move(optA).value());//选择1
f(std::move(optA.value());//选择2
std::cout在std::move(optA.value()
和std::move(optA.value())
之间没有区别value()
只返回一个引用所包含值的glvalue,您可以让value()
返回一个左值,然后通过std::move
将其转换为xvalue,或者您可以先调用std::move
并让value()
立即给您一个xvalue(实际上,从lvalue到xvalue的转换将发生在value()
方法本身的某个地方)。在这种简单的情况下,ref限定的重载显然不是很有用,但是当通过转发引用O&&O
传递可选值,并且您希望std::forward(O).value()
传递到“做正确的事”。std::move(optA).value()
和std::move(optA.value())
value()
只返回一个引用包含值的glvalue,您可以使用value()
返回一个左值,然后通过std::move
将其转换为xvalue,或者您可以先调用std::move
,让value()
立即给您一个xvalue(实际上,从左值到xvalue的转换将发生在value()
方法本身的某个地方)。在这种简单的情况下,ref限定的重载显然不是很有用,但是当通过转发引用O&&O
传递可选内容,并且您希望std::forward(O).value()
来“做正确的事情”时,它可能会很有用
您“移动了”optA
,但这并不意味着您更改了它。通常,您不应该在它“移出”后使用值,因为它处于有效但不确定的状态。std::move
只是一个类型转换(与静态转换(optA)
完全相同).Moving构造函数未被调用,因为未创建std::optional
的新实例。因此具有\u值
返回true
在这种情况下,确实调用了T&&value()&&
f(std::move(optA.value())); // OPTION 2
这里不调用T&&value()&&
,因为optA
不是&
。所以您得到A&
,并通过std::move
将其强制转换为A&&
,然后传递到f
,它可能不起任何作用。optA
没有更改,但仍然报告它包含值
您“移动了”optA
,但这并不意味着您更改了它。通常,您不应该在它“移出”后使用值,因为它处于有效但不确定的状态。std::move
只是一个类型转换(与静态转换(optA)
完全相同).Moving构造函数未被调用,因为未创建std::optional
的新实例。因此具有\u值
返回true
在这种情况下,确实调用了T&&value()&&
f(std::move(optA.value())); // OPTION 2
这里不调用T&&value()&&
,因为optA
不是&
。所以您得到A&
,并通过std::move
将其强制转换为A&&
,然后传递到f
,它可能不起任何作用。optA
没有更改,但仍然报告它包含值
是否存在value()&&与std::move和value()不同的情况
考虑以下几点:
optional<A> func() {...}
void f(optional<A> opt) {...}
void g(A a) {...}
f(func());
g(func().value());
h
的参数是由move初始化的。为什么?因为如果访问prvalue的成员子对象,结果表达式是xvalue,因此可以在不显式使用std::move
的情况下进行移动
optional
的&&
构造函数确保value
以同样的方式工作。如果在prvalue临时调用它,那么它将返回一个xvalue,无需显式的std::move
调用就可以从中移动它。因此在原始情况下,g
的参数是通过move初始化的,这与它将要做的完全一样如果它正在访问prvalue的成员子对象,则具有
是否存在value()&&与std::move和value()不同的情况
考虑以下几点:
optional<A> func() {...}
void f(optional<A> opt) {...}
void g(A a) {...}
f(func());
g(func().value());
h
的参数是由move初始化的。为什么?因为如果访问prvalue的成员子对象,结果表达式是xvalue,因此可以在不显式使用std::move
的情况下进行移动
optional
的&&
构造函数确保value
以同样的方式工作。如果在prvalue临时调用它,那么它将返回一个xvalue,无需显式的std::move
调用就可以从中移动它。因此在原始情况下,g
的参数是通过move初始化的,这与它将要做的完全一样如果它正在访问一个prvalue的成员子对象,那么它就有了。我假设两个版本是相同的,但是,Nicol Bolas的答案看起来很有趣
假设它们都产生右值,代码也不重要,我们没有理由忽略可读性
std::move(optA).value()
这表明您移动了变量,并且依赖于类的编写器来提供正确的重载。如果缺少,您可能会错过
一些过梁也认为这是一个变量,不应该再碰它了,这会阻止你在移动后使用它
std::move(optA.value())
但是,它将返回值转换为rValuy。我主要把这个代码看作是一个bug。函数要么按值返回(要么const引用),然后我们写入许多代码。否则,函数返回一个LValk,并且可能破坏了