2

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

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
}

因为为未知数调用 getValueAstring_id会导致错误,所以通常同时调用这两个命令:

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),如果在数据库中则返回 true string_id,并将映射的值分配给val.

这两个getValue函数的内容几乎相同,所以我想getValueBgetValueA. 这是我超出我的深度的地方,通过这种尝试:

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在这个结构中吗?

我倾向于改变getValueBconst int& getValueB(std::string string_id, const bool value_exists_in_db)解开这个混乱,但我有兴趣找出什么是可能的以及我哪里出错了。


编辑:请注意const int& getValueA(std::string string_id),理想情况下,不应更改的声明以避免更改代码库的其余部分。

4

5 回答 5

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;
}
于 2019-04-19T05:48:53.907 回答
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}

于 2019-04-19T03:45:45.993 回答
2

返回intby const 引用是一种悲观。 int复制成本低;与作为引用基础的指针一样便宜或更便宜。出于这个原因,我会说正确的做法是更改getValueA为按值返回,然后您可以按照以下方式实现它getValueB

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

另一种选择是将两者结合起来,并返回 a 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&. 复制字符串可能很昂贵,因此在这种情况下使用引用是有意义的。

于 2019-04-19T03:46:15.620 回答
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并使用该引用为变量分配一个有效值。

于 2019-04-19T03:53:01.167 回答
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
}
于 2019-04-19T03:35:58.220 回答