假设我有一个类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_;
};传递给Option::getOrElse的参数是在此Option为空时返回的默认值:
Option<int> x;  // empty
int y = 123;
x.getOrElse(y);  // == 123但是,我认为以下代码不安全:
Option<int> x;
x.getOrElse(123);  // reference to temporary variable!一种更安全的方法是从Option::getOrElse返回值,但如果Option不是空的,那将是浪费的。我能设法解决这个问题吗?
更新:我正在考虑如何重载getOrElse的参数类型(lvalue/rvalue),但还没有确切地知道如何做到这一点。
更新2:,也许这个?
T getOrElse(T &&x) const { ... }
const T & getOrElse(const T &x) const { ... }但我认为这可能是模棱两可的,因为lvalue和rvalue参数都符合第二个版本。
发布于 2016-05-03 00:50:52
但是,我认为以下代码不安全: 选项x;x.getOrElse(123);//引用临时变量!
你是对的。这就是为什么std::optional::value_or()返回T而不是T&或T const&的原因。根据N3672中的基本原理
有人认为,函数应该通过不断引用而不是值来返回,这将避免在某些情况下复制开销: 无效观察(const& x);可选ox { /* . */ };观察( ox.value_or(X{args}) );//不必要的副本 但是,只有当可选对象作为临时对象(没有名称)提供时,函数value_or的好处才是可见的;否则,三元操作符同样有用: 可选ox { /* .* };观察(ox?*ok : X{args});//无副本 此外,如果可选对象被断开,通过引用返回可能会呈现一个悬空引用,因为第二个参数通常是临时的: 可选的ox {nullopt};auto&x= ox.value_or(X{args});cout << x;// x正在悬挂!
我建议你遵循同样的准则。如果您确实需要避免复制,请使用三元。这是安全和无版权的:
Optional<int> ox = ...;
const int& val = ox ? *ox : 123;如果您真的没有,或者Optional是一个rvalue,那么getOrElse()就更简洁了。
发布于 2016-05-03 01:14:39
由于类的用户只能期望Option::get()返回的引用与Option对象生命周期的特定实例一样有效,因此您可以合理地对从Option::getOrElse()返回的内容做出同样的期望。
在这种情况下,对象维护它需要为客户端保持活动的事物集合可能是一种可接受的开销:
#include <list>
#include <memory>
#include <iostream>
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
    {
        if (val_ == nullptr) {
            std::cout << "storing const T &\n";
            elses_.push_front(x);
            return elses_.front();
        }
        return *val_;
    }
    const T & getOrElse(T &&x) const
    {
        if (val_ == nullptr) {
            std::cout << "storing T && by move\n";
            elses_.push_front(std::move(x));
            return elses_.front();
        }
        return *val_;
    }
private:
    std::shared_ptr<T> val_;
    mutable std::list<T> elses_;
};
int main()
{
    Option<int> x;  // empty
    int y = 123;
    auto rx = x.getOrElse(y);  // == 123
    auto & rxx = x.getOrElse(42); 
    std::cout << "rx = " << rx << "\n";
    std::cout << "rxx = " << rxx << "\n";
}只要从Option::getOrElse()返回的引用是有效的,那么Option::get()返回的引用就会有效。当然,这也意味着Option::getOrElse()可以抛出异常。
作为一个小小的改进,如果可以将T类型用作关联容器的键,则可以使用其中的一个而不是std::list,并且很容易地避免存储重复项。
发布于 2016-05-04 07:12:47
我可以建议重新设计这门课吗?
它有一个默认的ctor,它可以将val_保留为nullptr,但是它同时有一个get(),它可能会因为dereference (*)抛出异常。它还设计在shared_prt中保存T,但作为参考返回它。
让客户端知道它是空的:
template<typename T>
class Option {
public:
    Option() noexcept
    {}
    Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
    {}
    const T & get() const
    {
        return *val_;
    }
    bool IsNull() const
    {
        return val_ == nullptr;
    }
private:
    std::shared_ptr<T> val_;
};客户端代码更改如下:
Option option;
const T & ref = option.getOrElse(123);将是:
Option option;
const T & ref = option.IsNull() ? 123 : option.get();为什么我删除: if (val_ == nullptr) {
让我们澄清一下make_shared<>:
所以IsNull()也是无用的,它应该是这样的:
template<typename T>
class Option {
public:
    Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
    {}
    const T & get() const
    {
        return *val_;
    }
private:
    std::shared_ptr<T> val_;
};为什么要使用shared_ptr?选项对象可以多次移动或复制吗?或者我更喜欢这样的设计:
template<typename T>
class Option {
public:
    Option(T val) noexcept : val_(std::move(val))
    {}
    const T & get() const
    {
        return val_;
    }
private:
    T val_;
};https://stackoverflow.com/questions/36993646
复制相似问题