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

optional<reference_wrapper<T>> vs. optional<T>& - practical examples?

我读过 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>&。我不清楚语义差异,尤其是它们在实践中的利用方式。

std::optional<T> & 是对可以拥有 T 对象的可选对象的引用。您可以改变 T(如果包含一个)或者您可以清除通过引用传入的可选对象,破坏包含的 T.


std::optional<std::reference_wrapper<T>> 是一个可选对象,它可以拥有对 T 的引用,但它实际上并不拥有 T 本身。 T 生活在 std::optional 对象的 之外 。您可以改变 T(如果包含引用)或者您可以清除可选对象,这 不会 破坏 T。您还可以使可选对象指向不同的 T,但这有点毫无意义,因为调用者正在向您传递一个可选的 by 值。

请注意,我们已经 有一个内置于语言的类型,表示 "optional reference to a T":T*。原始指针和可选引用具有基本相同的语义:要么什么也得不到,要么得到不属于你的对象的句柄。在现代 C++ 中,原始指针是表达接收者不拥有的可选值的方式。

我想不出我曾经明确使用 std::optional<std::reference_wrapper<T>> 而不是 T* 的单一原因。

But why not just do this?

因为您的代码无法编译。 make_optional returns 纯右值,并且不能将纯右值传递给采用非 const 左值引用的函数。

这很重要,因为它显示了这两种情况之间的根本区别。如果您已经有一个 T 或来自其他地方的对 T 的引用,那么您 不能 将其传递给接受 optional<T>& 的函数.您必须将 T 复制到 optional<T> 变量中,然后将对 optional 变量的引用传递给函数。

你无法修改外界的T。这就是不同之处:使用 reference_wrapper<T>,您可以。

或者,如果您有一个函数可以使用或不使用可修改的 T,您可以像大多数人一样传递 T*

正如其他答案所述,这两种类型有不同的用途,因为引用是指不同的事物(在一种情况下引用 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