因此,我已经解决了这个问题,但是如果我做的是最佳做法,则需要您的意见。
一个简单的类包含一个unique_ptrs
vector 来对对象进行排序。我将在下面解释成员变量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
。但是,我不会将对象移出 vector ,所以我将unique_ptr
返回为const ref。我的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>
的null_unique
成员变量,并在find找不到订单时将其返回。这解决了问题,并且警告消失了,并且在找不到订单时也不会崩溃。但是,我怀疑我的解决方案是否会使我的课变得丑陋。这是处理这种情况的最佳实践吗?
最佳答案
在这里返回unique_ptr
,引用或其他方式实际上没有任何意义。 unique_ptr
暗含对该对象的所有权,而这些实际上并不是该代码所传达的语义。
如注释中所建议,只要您的项目设计明确禁止您或您的团队中的任何人在资源拥有对象的析构函数的上下文之外调用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()
替换*opt
应该完全一样。只要我在这里,我可能会指出
std::vector<std::unique_ptr<type>>
对象具有非常“Code Smell”的意义。 std::vector<type>
隐含了对对象的所有权,因此,除非您有充分的理由更喜欢此对象(对象可能很大,不可移动/不可复制,并且您需要经常插入和删除条目?也许是多态类型?),否则将其简化为简单的std::vector
可能更好。编辑:
Boost版本略有不同,因为
boost::optional
对“可选引用”没有任何限制,这是C++标准库的std::optional
版本特别禁止的。 Boost版本实际上会稍微简单一些://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*/
}