optional<reference_wrapper<T>>和optional<T>& - 实际例子?

3

我了解std::optional<std::reference_wrapper<T>>,它可以用来传递可选的引用。

然而,我无法想到实际的应用场景,在这种情况下使用optional<T>&可能更为简单。

例如,假设我正在编写一个函数,需要通过引用传递可选的vector<int>

我可以这样做:

void f(optional<reference_wrapper<vector<int>>> v) {
    // ...
}

int main() {
    vector<int> v = {1, 2, 3, 4};
    f(make_optional<reference_wrapper<vector<int>>>(std::ref(v));
    return 0;
}

但为什么不这样做呢?

void f(optional<vector<int>>& v) {
    // ...
}

int main() {
    f(make_optional<vector<int>>>(std::initializer_list{1, 2, 3, 4}));
    return 0;
}

请给出一个例子,说明何时使用 optional<reference_wrapper<T>> 比使用 optional<T>& 更佳。它们的语义差异,尤其是在实践中可以利用的方式并不清楚。

考虑返回值。optional将是一个被返回的临时变量。 - ChrisMM
你在需要在这两者之间进行选择的情况是什么时候?它们有不同的含义......使用那个与问题相关的意思。 - Barry
3个回答

7
std::optional<T> &是一个可拥有T对象的optional对象的引用。你可以改变一个包含的T对象(如果存在),或者你可以清除通过引用传递的optional对象,销毁包含的T对象。
std::optional<std::reference_wrapper<T>>是一个可拥有对T的引用的optional对象,但实际上它并不拥有T本身。该T存在于std::optional对象之外。你可以改变T(如果包含其引用),或者你可以清除optional对象,这不会销毁T。你还可以将optional对象指向另一个T,但这样做可能没有意义,因为调用者是按值传递可选项。
请注意,语言中已经内置了一种类型,表示“对T的可选引用”: T*。原始指针和可选引用具有基本相同的语义:你要么什么都不得到,要么得到一个你不拥有的对象的句柄。在现代C++中,原始指针是表示接收方未拥有的可选值的方式。
我无法想出一个明确使用std::optional<std::reference_wrapper<T>>而不是T*的单一原因。

1
我理解 - optional<T>& x = ... 强制 xT 对象拥有生命周期所有权,这意味着如果我们已经有一个 T 对象,我们必须在 x 中复制它。 optional<reference_wrapper<T>>& 不拥有 T 对象,因为 reference_wrapper<T> 不拥有 T 对象,因此当我们已经有现有的 T 对象时可以使用它(但在这种情况下,最好使用裸指针 T*)。一切清楚,谢谢。 - Aviv Cohn
@AvivCohn 是的,就是这样! - cdhowie

2

为什么不这样做呢?

因为你的代码无法编译。 make_optional 返回一个 prvalue,而你无法将 prvalue 传递给需要非 const 左值引用的函数。

这很重要,因为它展示了这两种情况的根本区别。如果你已经有一个 T 或者从其他地方拥有一个引用,那么你就不能将它传递给一个需要 optional<T>& 的函数。你必须将 T 复制到一个 optional<T> 变量中,然后将 optional 变量的引用传递给函数。

你将无法修改外部世界的 T。这就是区别:使用 reference_wrapper<T> 就可以修改。

或者如果你有一个可以在可修改或不可修改的情况下工作的函数,你可以像大多数人一样只传递一个 T*


我明白了 - 这是一个所有权的问题。optional<T> 拥有 T 对象的生命周期(而 optional<T>& 只是对 optional<T> 的引用),而 optional<reference_wrapper<T>> 则拥有 reference_wrapper<T> 的生命周期,但不拥有 T 对象的生命周期。因此有不同的语义。谢谢! - Aviv Cohn

1
正如其他答案所述,这两种类型有不同的用途,因为引用是针对不同的东西(一个引用到一个optional,另一个引用到一个vector)。不要重复解释,这里有一些代码可以让你玩耍,以查看功能差异。
#include <iostream>
#include <vector>
#include <functional>
#include <optional>


// For better readability:
using optional_reference_vector = std::optional<std::reference_wrapper<std::vector<int>>>;
using optional_vector           = std::optional<std::vector<int>>;


void f(optional_reference_vector v) {
    v->get().push_back(5);
}

void g(optional_vector & w) {
    w->push_back(5);
}

int main() {
    // Two identical vectors with which to work:
    std::vector<int> v = {1, 2, 3, 4};
    std::vector<int> w = {1, 2, 3, 4};

    // Demonstrate an optional reference to a vector
    // ---------------------------------------------
    // Create a reference to `v` in `opt_v`.
    // Changes to `opt_v` will be reflected in `v` (and vice versa).
    optional_reference_vector opt_v {std::ref(v)};
    v.clear();
    // A copy of `opt_v` will be made in f(). Since we are copying a reference to
    // a vector and not the vector itself, the vector in main() is changed by f().
    f(opt_v);
    // Both `v` and `opt_v` refer to the same vector, so the size is the same.
    std::cout << "Using a reference to the vector:\n"
              << "Original vector size: " << v.size() << '\n'
              << "Optional vector size: " << opt_v->get().size() << "\n\n";

    // Demonstrate a reference to an optional vector
    // ---------------------------------------------
    // Copy `w` into `opt_w`.
    // Changes to `opt_w` have no effect on `w` (and vice versa).
    optional_vector opt_w {w};
    w.clear();
    // A reference to `opt_w` will be used in g(), so `opt_w` is updated.
    g(opt_w);
    // There are two vectors that now have different sizes.
    std::cout << "Using a copy of the vector:\n"
              << "Original vector size: " << w.size() << '\n'
              << "Optional vector size: " << opt_w->size() << '\n';

    return 0;
}

这段代码的输出是:
Using a reference to the vector:
Original vector size: 1
Optional vector size: 1

Using a copy of the vector:
Original vector size: 0
Optional vector size: 5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接