简体   繁体   English

通过引用返回unique_ptr

[英]Return a unique_ptr by reference

So I've solved this problem, but I need your opinion if what I did is best practice. 所以我已经解决了这个问题,但如果我所做的是最佳实践,我需要你的意见。

A simple class holds a vector of unique_ptrs to order objects. 一个简单的类包含一个unique_ptrs向量来命令对象。 I will explain the member variable null_unique below. 我将在下面解释成员变量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);
    ....

So I need the users of this class to get access to the order unique_ptr if found. 因此,我需要此类的用户才能访问订单unique_ptr如果找到)。 However I'm not going to move the object out of the vector so I'm returning the unique_ptr as const ref. 但是我不打算将对象移出向量,所以我将unique_ptr作为const ref返回。 My implementation of the find_order method: 我的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;
}

Since I'm returning by reference I can't return a nullptr. 由于我通过引用返回,因此无法返回nullptr。 If I try to do so, I get warning : returning reference to a temporary . 如果我尝试这样做,我会收到warning : returning reference to a temporary And if nothing is found the program crashes. 如果没有找到任何结果,程序将崩溃。 So I added a unique_ptr<order> member variable called null_unique and I return it when find doesn't find an order. 所以我添加了一个名为null_uniqueunique_ptr<order>成员变量,当find找不到订单时我返回它。 This solves the problem and warning is gone and doesn't crash when no order is found. 这解决了问题并且警告消失了,并且在没有找到订单时不会崩溃。

However I'm doubting my solution as it make my class ugly. 但是我怀疑我的解决方案,因为它让我的课堂变得丑陋。 Is this the best practice for handling this situation? 这是处理这种情况的最佳做法吗?

You should only return and accept smart pointers when you care about their ownership semantics. 当您关心其所有权语义时,您应该只返回并接受智能指针 If you only care about what they're pointing to, you should instead return a reference or a raw pointer . 如果你只关心他们指向的是什么,你应该返回一个引用或一个原始指针

Since you're returning a dummy null_unique , it is clear that the caller of the method doesn't care about the ownership semantics. 由于您返回一个虚拟的null_unique ,很明显该方法的调用者不关心所有权语义。 You can also have a null state : you should therefore return a raw pointer : 你也可以有一个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();
}

It doesn't really make sense to return a unique_ptr here, reference or otherwise. 在这里,引用或其他方式返回unique_ptr并没有多大意义。 A unique_ptr implies ownership over the object, and those aren't really the semantics being conveyed by this code. unique_ptr意味着拥有该对象,而这些都不是真正的语义被这种代码传送。

As suggested in the comments, simply returning a raw pointer is fine here, provided that your Project Design explicitly prohibits you or anyone on your team from calling delete or delete[] outside the context of the destructor of a Resource-owning object. 正如评论中所建议的那样,只要您的Project Design明确禁止您或团队中的任何人在资源拥有对象的析构函数的上下文之外调用deletedelete[] ,只需返回原始指针即可。

Alternatively, if you either have access to Boost or C++17, a std::optional<std::reference_wrapper<order>> might be the ideal solution. 或者,如果您可以访问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*/
}

(EDIT: In testing on the most recent version of MSVC 2017, it looks like std::reference_wrapper<T> will happily do an implicit conversion to T& if you tell it to. So replacing opt->get() with *opt should work exactly the same.) (编辑:在MSVC 2017的最新版本测试中,看起来std::reference_wrapper<T>会很高兴地隐式转换为T&如果你告诉它。所以用*opt替换opt->get()工作完全一样。)

As long as I'm here, I might point out that a std::vector<std::unique_ptr<type>> object has a very "Code Smell" sense to it. 只要我在这里,我可能会指出std::vector<std::unique_ptr<type>>对象具有非常“Code Smell”的意义。 std::vector<type> implies ownership of the object as is, so unless you have a good reason to prefer this (maybe the objects are large, unmovable/uncopyable, and you need to insert and remove entries frequently? Maybe this is a polymorphic type?), you're probably better off reducing this to a simple std::vector . std::vector<type>意味着对象的所有权,所以除非你有充分的理由喜欢这个(可能是对象很大,不可移动/不可复制,你需要经常插入和删除条目吗?也许这是一个多态类型?),你可能最好还原到一个简单的std::vector

EDIT: 编辑:

The boost version is subtly different, because boost::optional has no restrictions against "optional references", which are specifically forbidden by the C++ Standard Library's version of std::optional . boost版本略有不同,因为boost::optional对“可选引用”没有限制,这是C ++标准库版本的std::optional特别禁止的。 The boost version is actually going to be slightly simpler: 升级版本实际上会稍微简单一些:

//return type changes, nothing else changes
boost::optional<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.
}

/*...*/

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM