通过引用返回unique_ptr

时间:2017-04-13 20:05:00

标签: c++ c++11

所以我已经解决了这个问题,但如果我做的是最佳实践,我需要你的意见。

一个简单的类将unique_ptrs的向量保存到订单对象。我将在下面解释成员变量null_unique

class order_collection {
    typedef std::unique_ptr<order> ord_ptr;
    typedef std::vector<ord_ptr> ord_ptr_vec;
    ord_ptr_vec orders;
    ord_ptr null_unique;
public:
    ...
    const ord_ptr & find_order(std::string);
    ....

因此,如果找到,我需要此类用户访问订单unique_ptr。但是我不打算将对象移出向量,所以我将unique_ptr作为const引用返回。我对find_order方法的实现:

const order_collection::ord_ptr & order_collection::find_order(std::string id) {
    auto it = std::find_if(orders.begin(),orders.end(),
                           [&](const order_collection::ord_ptr & sptr) {
                               return sptr->getId() == id;
                           });

    if (it == orders.end())
        return null_unique; // can't return nullptr here
    return *it;
}

由于我通过引用返回,因此无法返回nullptr。如果我尝试这样做,我会得到warning : returning reference to a temporary。如果没有找到任何结果,程序将崩溃。所以我添加了一个名为unique_ptr<order>的{​​{1}}成员变量,当find找不到订单时我将其返回。这解决了问题并且警告消失了,并且在没有找到订单时不会崩溃。

然而,我怀疑我的解决方案因为它让我的课堂变得丑陋。这是处理这种情况的最佳做法吗?

2 个答案:

答案 0 :(得分:5)

当您关心其所有权语义时,您应该只返回并接受智能指针。如果您只关心他们指向的内容,则应该返回引用原始指针

由于您返回一个虚拟null_unique,很明显该方法的调用者不关心所有权语义。您还可以拥有 null状态:因此您应该返回原始指针

order* order_collection::find_order(std::string id) {
    auto it = std::find_if(orders.begin(),orders.end(),
                           [&](const order_collection::ord_ptr & sptr) {
                               return sptr->getId() == id;
                           });

    if (it == orders.end())
        return nullptr;
    return it->get();
}

答案 1 :(得分:4)

在此处,引用或其他方式返回unique_ptr没有意义。 unique_ptr意味着对对象的所有权,并且那些真正这个代码传达的语义。

正如评论中所建议的那样,只需返回原始指针即可,只要您的Project Design明确禁止您或团队中的任何人在析构函数的上下文之外调用deletedelete[]拥有资源的对象。

或者,如果您有权访问Boost或C ++ 17,std::optional<std::reference_wrapper<order>>可能是理想的解决方案。

std::optional<std::reference_wrapper<order>> order_collection::find_order(std::string id) {
    auto it = std::find_if(orders.begin(),orders.end(),
                       [&](const order_collection::ord_ptr & sptr) {
                           return sptr->getId() == id;
                       });

    if (it == orders.end())
        return {}; //empty optional object
    return **it; //will implicitly convert to the correct object type.
}

/*...*/

void func() {
    auto opt = collection.find_order("blah blah blah");
    if(!opt) return;
    order & ord = opt->get();
    /*Do whatever*/
}

(编辑:在MSVC 2017的最新版本测试中,如果您告诉它,std::reference_wrapper<T>将很乐意隐式转换为T&。所以替换{{带有opt->get()的1}}应该完全相同。)

只要我在这里,我可能会指出*opt对象具有非常“代码嗅觉”的感觉。 std::vector<std::unique_ptr<type>>意味着对象的所有权,所以除非你有充分的理由喜欢这个(也许对象很大,不可移动/不可复制,你需要经常插入和删除条目?也许这是一个多态类型?),你可能最好将其减少到一个简单的std::vector<type>

编辑:

boost版本略有不同,因为std::vector对“可选引用”没有限制,C ++标准库的boost::optional版本明确禁止使用。升级版本实际上会稍微简单一些:

std::optional