std :: optional:简单值与ref限定value()之间的有效区别

时间:2019-06-19 16:14:01

标签: c++ c++17 stdoptional

这是一个学术问题。 std::optional<T>类型具有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;

选项1和选项2之间有有意义的区别吗?对于选项1,我是否有10或未指定为输出?根据我的测试,在两种情况下has_value()仍然为真。

是否有可能value() &&std::movevalue()有所不同?

4 个答案:

答案 0 :(得分:4)

std::move(optA).value()std::move(optA.value())之间没有区别。 value()仅返回引用包含值的glvalue,或者您可以让value()返回一个左值,然后通过std::move将其转换为xvalue,或者可以调用{{1} }首先,让std::move立即给您一个xvalue(实际上,从lvalue到xvalue的转换将发生在value()方法本身内的某个位置)。 ref限定的重载在这种简单情况下显然不是很有用,但是当转发参考value()传递了可选参数并且您希望O&& o“做正确的事”时,有用。 / p>

答案 1 :(得分:3)

f(std::move(optA).value()); // OPTION 1

您“移动”了optA,但这并不意味着您对其进行了更改。通常,在“移出”值后,您不应再使用它,因为它处于有效但不确定的状态。 std::move只是类型转换(与static_cast<A&&>(optA)完全相同)。由于没有创建std::optional<A>的新实例,因此未调用移动构造函数。因此,has_value返回true

在这种情况下,确实调用了T && value() &&

f(std::move(optA.value())); // OPTION 2

T && value() &&在这里没有被调用,因为optA不是&&。因此,您得到A&并由A&&投射到std::move,然后传递到f,该行大概什么也不做。 optA未被更改,仍然报告它包含值。

答案 2 :(得分:1)

  

是否有可能value()&&与std :: move和value()有所不同?

请考虑以下内容:

optional<A> func() {...}

void f(optional<A> opt) {...}
void g(A a) {...}

f(func());
g(func().value());

f的{​​{1}}参数将通过移动来初始化。从技术上讲,它将直接由prvalue初始化,但是C ++ 17之前的版本意味着它将被移动初始化。该初始化可以省去,但是如果不是,则可以通过move完成。总是。

但是opt的参数呢?应该怎么办?好吧,考虑一下该怎么做:

g

struct C {string s;}; C func2() {...} void h(string s); h(func2().s); 的参数由 move 初始化。为什么?因为如果您访问prvalue的成员子对象,则结果表达式是xvalue,因此有资格进行移动而无需显式使用h

std::move的{​​{1}}构造函数可确保&&以相同的方式工作。如果在prvalue临时上调用它,则它将返回一个xvalue,无需显式optional调用即可将其移出。因此,在原始情况下,value的参数是通过move初始化的,就像访问访问prvalue的成员子对象时一样。

答案 3 :(得分:0)

我认为两个版本是相同的,但是,尼科尔·波拉斯的答案看起来很有趣。

假设它们都导致右值且代码无关紧要,那么我们就没有理由忽略可读性。

  std::move(optA).value()

这建议您移动变量,并依靠类的编写者提供正确的重载。如果丢失,您可能会错过。

某些短毛猫也将其视为不应该被触摸的变量,从而阻止您移动后使用。

std::move(optA.value())

但是这会将返回值转换为右值。我主要将此代码视为错误。函数要么按值(或const引用)返回,然后我们编写了很多代码。否则,该函数将返回一个左值,您可能会破坏该变量的内部状态。

因此,我建议一致地移动变量并以代码味道触发函数调用的移动。 (即使对于std :: optional,也无关紧要)