正确的方法来返回一个大对象或表明它没有找到

时间:2017-04-21 21:55:48

标签: c++ c++17

这种惯用的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副本?一旦返回可选项,一次从可选项中提取结果。要有更好的方法吗?

3 个答案:

答案 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的成员函数,并为该对象返回falselookupLargeObject将返回该对象以指示未找到真实对象:

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
}