避免返回by-reference参数

Ziz*_*Tai 8 c++ c++11

假设我有一个班级Option:

template<typename T>
class Option {
public:
    Option() noexcept
    {}

    Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
    {}

    const T & get() const
    {
        if (val_ == nullptr) {
            throw std::out_of_range("get on empty Option");
        }
        return *val_;
    }

    const T & getOrElse(const T &x) const
    {
        return val_ == nullptr ? x : *val_;
    }

private:
    std::shared_ptr<T> val_;
};
Run Code Online (Sandbox Code Playgroud)

传递给的参数Option::getOrElse是当它Option为空时返回的默认值:

Option<int> x;  // empty
int y = 123;
x.getOrElse(y);  // == 123
Run Code Online (Sandbox Code Playgroud)

但是,我认为以下代码不安全:

Option<int> x;
x.getOrElse(123);  // reference to temporary variable!
Run Code Online (Sandbox Code Playgroud)

更安全的方式是从值返回Option::getOrElse,但是当Option非空时,这将是浪费.我可以以某种方式解决这个问题吗?

更新:我正在考虑可能在参数类型(lvalue/rvalue)上重载getOrElse,但还没有弄清楚到底是怎么做的.

更新2:也许这个?

T getOrElse(T &&x) const { ... }

const T & getOrElse(const T &x) const { ... }
Run Code Online (Sandbox Code Playgroud)

但我认为这可能是模棱两可的,因为左值和右值都适合第二个版本.

Bar*_*rry 4

但是,我认为以下代码不安全:

Option<int> x;
x.getOrElse(123);  // reference to temporary variable!
Run Code Online (Sandbox Code Playgroud)

你是对的。这就是为什么std::optional::value_or()返回 aT而不是 a T&or的原因T const&。根据N3672中的基本原理:

有人认为函数应该通过常量引用而不是值返回,这可以避免某些情况下的复制开销:

void observe(const X& x);

optional<X> ox { /* ... */ };
observe( ox.value_or(X{args}) );    // unnecessary copy
Run Code Online (Sandbox Code Playgroud)

然而,只有当可选对象作为临时对象(没有名称)提供时,函数 value_or 的好处才可见;否则,三元运算符同样有用:

optional<X> ox { /* ... */ };
observe(ox ? *ok : X{args});            // no copy
Run Code Online (Sandbox Code Playgroud)

此外,如果可选对象被解除,通过引用返回可能会呈现悬空引用,因为第二个参数通常是临时的:

optional<X> ox {nullopt};
auto&& x = ox.value_or(X{args});
cout << x;                              // x is dangling!
Run Code Online (Sandbox Code Playgroud)

我建议您遵循相同的准则。如果您确实需要避免复制,请使用三元。这是安全且无复制的:

Optional<int> ox = ...;
const int& val = ox ? *ox : 123;
Run Code Online (Sandbox Code Playgroud)

如果你真的不这样做,或者Optional无论如何它是一个右值,getOrElse() 那就更简洁了。