从以其为参数的函数中返回引用

时间:2019-04-19 03:31:12

标签: c++

从概念上讲,我有一门课可以做到这一点。请注意,底层数据类型在实际应用中更为复杂。这只是为了简化:

class A
{
  private:
  std::map<std::string, int> database;

  public:
  bool knowsValue(std::string string_id);  // Returns true if string_id is in database
  const int& getValueA(std::string string_id);  // Returns reference to int mapped to string_id in database
}

由于为未知的string_id调用getValueA会导致错误,因此通常会同时调用两个命令:

if obj.knowsValue(string_id)
  int val = obj.getValueA(string_id)
else
  std::cout << "Value does not exist in database";

因为这需要对数据库对象执行两个后续的find操作,所以我做了此函数bool getValueB(std::string string_id, int& val),如果string_id在数据库中,则该函数返回true,并将映射值分配给{ {1}}。

两个val函数的内容几乎相同,因此我想重用getValue中的getValueB。通过这种尝试,这就是我无法做到的地方:

getValueA

显然,const int& getValueA2(std::string string_id) { static int val_tmp; // If not static, this object is deleted after the function call and the reference becomes invalid if (getValueB(string_id, val_tmp)) return static_cast<const int&>(val_tmp); else return static_cast<const int&>(0); } 关键字在此处不合适,因为不应在函数之间共享该值。同样,引用不是static中的const的事实也不是最优的。

我的问题:

  • 编写getValueB(试图返回它在参数中获得的引用)的正确方法是什么?中间的getValueA2似乎很邪恶。
  • 引用在这个结构中可以完全是val_tmp吗?

我倾向于将const更改为getValueB来消除这种混乱,但是我将对找出可能的情况以及出了问题的地方感兴趣。


edit:请注意,const int& getValueB(std::string string_id, const bool value_exists_in_db)的声明最好不要更改,以免更改其余代码库。

5 个答案:

答案 0 :(得分:3)

我认为,从根本上讲,您不应该尝试将两者结合起来,因为它们实际上在做完全不同的事情。

一个参数版本(const int& getValueA(const std::string&))返回对某个值的引用。

两个自变量版本(bool getValueA2(const std::string&, int&))将值的副本分配给调用方提供的新位置。

您在这里有几个选择:

  1. 将实现分开放置。
  2. 将两个参数的版本稍微更改为类似bool getValueA2(const std::string &, const int *&)的形式,以巩固功能。这很丑陋,而且基本上没有吸引力。
  3. 照别人说的做,改变签名。
  4. 重构这两种方法以使用实际上包含通用功能的第三个辅助方法。

如果您去#4,它可能看起来像这样(请注意,由于您没有提供足够的详细信息让我这样做,因此我无法提供更相关的示例):

auto getValueAImpl(const std::string &key) const {
    return database.find(key);
}

const int &getValueA(const std::string &key) {
    auto it = getValueAImpl(key);
    if (it != database.end()) return it->second;
    throw std::runtime_error("key not found");
}

bool getValueA(const std::string &key, int &val) {
    auto it = getValueAImpl(key);
    if (it == database.end()) return false;
    val = it->second;
    return true;
}

答案 1 :(得分:2)

如果您可以选择使用C ++ 17,我建议您使用std::optional<int>作为返回类型。

std::optional<int> getValueA2(std::string string_id)
{
   auto iter = database.find(id);

   // If found, return the value.
   if ( iter != database.end() )
   {
      return {iter->second}; // Equivalent to std::optional<int>{iter->second};
   }

   else
   {
      // Not found. Return a default constructed object
      return {};
   }
}

如果您不能使用C ++ 17,则可以使用std::pair<bool, int>作为返回类型,以穷人代替std::optional<int>

如果找到该值,则返回{true, iter->second}
如果找不到该值,则返回{false, 0}

答案 2 :(得分:2)

通过const引用返回int是一种悲观。 int s复制很便宜;比引用所依据的指针便宜或便宜。出于这个原因,我想说的正确做法是将getValueA更改为按值返回,然后您可以根据getValueB来实现它:

int getValueA(const std::string& string_id)
{
  int val_tmp;
  if (getValueB(string_id, val_tmp))
    return val_tmp;
  else
    return 0;
}

另一种选择是将两者合并,然后返回std::optional<int>

std::optional<int> getValue(const std::string& string_id)
{
  auto it = database.find(string_id);
  if (it == database.end()) {
    return std::nullopt;
  } else {
    return *it;
  }
}

注意:我也将参数类型更改为const std::string&。复制字符串可能会很昂贵,因此在这种情况下使用引用很有意义。

答案 3 :(得分:1)

const int* getValuePtr(std::string const& id) const
{
  auto it = database.find(id);
  if(it==database.end()) return nullptr;
  return &(it->second);
}

指针是一个可能的引用。

使用时:

if(auto x=foo.getValuePtr("bob")){
  // use *x here safely
}

答案 4 :(得分:1)

如果您的函数返回了引用,则该函数需要确保返回的引用用于调用方使用它时仍然存在的内容。

在您的示例中;

const int& getValueA2(std::string string_id)
{
   static int val_tmp;  // If not static, this object is deleted after the function call and the reference becomes invalid
   if (getValueB(string_id, val_tmp))
     return static_cast<const int&>(val_tmp);
   else
     return static_cast<const int&>(0);
}

然后,val_tmp必须是您所拥有的static,或者是以某种方式分配的,以便它继续存在。如果调用者使用返回的引用,则删除static关键字将导致调用者具有未定义的行为-因为就您的程序而言,当函数返回时,val_tmp将不复存在。

由于您希望函数以某种方式获取值,因此更好的选择是按值而不是引用返回。

int getValueA2(std::string string_id)
{
   int val_tmp;
   if (getValueB(string_id, val_tmp))
     return val_tmp;
   else
     return 0;
}

这很好,假设getValueB()通过引用接受val_tmp,并使用该引用将有效值分配给变量。