理解关于 EMC++ Item 41 的勘误注释

问题描述

在第 41 条中,Scott Meyers 编写了以下两个类:

class Widget {
public:
  void addName(const std::string& newName)   // take lvalue;
  { names.push_back(newName); }              // copy it

  void addName(std::string&& newName)        // take rvalue;
  { names.push_back(std::move(newName)); }   // move it; ...

private:
  std::vector<std::string> names;
};
class Widget {
public:
  template<typename T>                            // take lvalues
  void addName(T&& newName)                       // and rvalues;
  {                                               // copy lvalues,names.push_back(std::forward<T>(newName)); }  // move rvalues;
  }                                               // ...
private:
  std::vector<std::string> names;
};

评论中写的是正确的,即使这并不意味着这两种解决方案完全相同,书中确实讨论了一些差异。

In the errata,然而,作者评论了书中未讨论的另一个差异:

(1) 左值和右值的重载与 (2) 采用通用引用 (uref) 的模板之间的一个行为差异是左值重载声明了其参数 const,而 uref 方法则没有。这意味着在左值重载的参数上调用函数将始终是 const 版本,而在 uref 版本的参数上调用函数只有在传入的参数为 const 时才会是 const 版本.换句话说,非 const 左值参数可能会在重载设计中产生与 uref 设计不同的行为。

但我不确定我是否理解。

实际上,写这个问题我可能已经理解了,但我没有写答案,因为我仍然不确定。

可能作者是说当一个const左值传递给addName时,第一个代码中的newNameconst,非{{1} }} 在第二个代码中,这意味着 如果 const 被传递给另一个函数(或者在其上调用一个成员函数),那么该函数将需要接受一个 newName 参数(或成为 const 成员函数

我的解释是否正确?

但是,我没有看到这在具体示例中有何不同,因为没有在 const调用成员函数也没有将其传递给具有不同重载 {{ 1}} 和非 newName 参数(不完全是:std::vector<T>::push_backconst 参数和 const 参数有两个重载,但左值仍然会绑定仅适用于前一个重载...)。

解决方法

在第二种情况下,当一个 const std::string 左值被传递给模板时

  template<typename T>
  void addName(T&& newName)
  { names.push_back(std::forward<T>(newName)); }

实例化结果如下(我删除了 std::forward 调用,因为它实际上是一个无操作)

  void addName(const std::string& newName)
  { names.push_back(newName); }

而如果传递了 std::string 左值,则 addName 的结果实例是

  void addName(std::string& newName)
  { names.push_back(newName); }

这意味着const版本的std::vector<>::push_back被称为

在第一个场景中,当 std::string 左值传递给 addName 时,选择第一个重载不管 const-ness

  void addName(const std::string& newName)
  { names.push_back(newName); }

这意味着在两种情况下都选择了 conststd::vector<>::push_back 重载。