这种惯用的C ++方式是什么? 我有一个看起来像这样的方法:
crawler.spider.fetch('http://stackoverflow')
# ctrl+d to exit and wait for fetch to trigger.
这是错误的,因为如果你用一个不存在的id调用它,它将创建一个大对象的新实例并将其放入容器中。我不希望这样。我也不想抛出异常。我希望返回值表示未找到该对象(因为它或多或少是正常情况)。 所以我的选择是指针或可选。指针我理解和喜欢,但感觉C ++不想让我再使用指针。 那么选择权。我将返回一个可选项,然后调用者看起来像这样:
LargeObject& lookupLargeObject(int id) {
return largeObjects[id];
}
这是对的吗?感觉有点糟糕,因为我似乎在这里创建了2个LargeObject副本?一旦返回可选项,一次从可选项中提取结果。要有更好的方法吗?
答案 0 :(得分:4)
由于您不想返回指针,但也不想抛出异常,并且您可能想要引用语义,最简单的方法是返回std::optional<std::reference_wrapper<LargeObject>>
。
代码如下所示:
std::optional<std::reference_wrapper<LargeObject>> lookupLargeObject(int id) {
auto iter = largeObjects.find(id);
if (iter == largeObjects.end()) {
return std::nullopt;
} else {
return std::ref(iter->second);
}
}
使用C ++ 17,您甚至可以在iter
条件中声明if
变量。
调用查找函数并使用引用然后看起来像这样(这里有if
中的变量声明 - 条件):
if (auto const lookup_result = lookupLargeObject(42); lookup_result) {
auto& large_object = lookup_result.value().get();
// do something with large_obj
} else {
// deal with it
}
答案 1 :(得分:1)
有两种方法不需要使用指针 - 使用sentinel对象,并接收引用,而不是返回它。
第一种方法依赖于将LargeObject
的特殊实例指定为“无效”实例 - 例如,通过创建名为isValid
的成员函数,并为该对象返回false
。 lookupLargeObject
将返回该对象以指示未找到真实对象:
LargeObject& lookupLargeObject(int id) {
if (largeObjects.find(id) == largeObjects.end()) {
static LargeObject notFound(false);
return notFound;
}
return largeObjects[id];
}
第二种方法传递参考,而不是接收它:
bool lookupLargeObject(int id, LargeObject& res) {
if (largeObjects.find(id) == largeObjects.end()) {
return false;
}
res = largeObjects[id];
return true;
}
答案 2 :(得分:0)
如果LargeObject
不需要默认构造lookupLargeObject
,无论它是否昂贵或不具有语义意义,您都可以使用std:map::at
成员函数。
LargeObject& lookupLargeObject(int id) {
return largeObjects.at(id);
}
如果您愿意在调用函数中使用if-else
代码块,我会将函数的返回类型更改为LargeObject*
。
LargeObject* lookupLargeObject(int id) {
auto it = largeObjects.find(id);
if ( it == largeObjects.end() )
{
return nullptr;
}
return &(it->second);
}
然后,客户端代码可以是:
LargeObject* result = lookupLargeObject(42);
if (result) {
// Use result
} else {
// deal with it
}