所以我已经解决了这个问题,但如果我做的是最佳实践,我需要你的意见。
一个简单的类将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找不到订单时我将其返回。这解决了问题并且警告消失了,并且在没有找到订单时不会崩溃。
然而,我怀疑我的解决方案因为它让我的课堂变得丑陋。这是处理这种情况的最佳做法吗?
答案 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明确禁止您或团队中的任何人在析构函数的上下文之外调用delete
或delete[]
拥有资源的对象。
或者,如果您有权访问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