【问题标题】:Understanding comment from the errata about Item 41 of EMC++了解关于 EMC++ 第 41 项的勘误表的评论
【发布时间】:2021-04-12 13:52:16
【问题描述】:

在第 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;
};

cmets 中写的都是正确的,即使这根本不意味着这两种解决方案是等价的,而且书中确实讨论了一些差异。

In the errata,然而,作者发现了书中没有讨论的另一个区别:

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

但我不确定我是否理解它。

其实写这个问题我大概已经看懂了,但是我还不确定,所以我不写答案。

大概作者是说当一个非const的左值被传递给addName时,newName在第一个代码中是const,而在第二个代码中是非const,也就是说如果 newName 被传递给另一个函数(或在其上调用了成员函数),则该函数将需要采用 const 参数(或成为 const 成员函数) .

我的解释是否正确?

但是,我看不出这在具体示例中有何不同,因为在 newName 上没有调用任何成员函数,也没有将它传递给对 const 和非const 参数 (不完全是:std::vector&lt;T&gt;::push_back 有两个重载 const T&amp; 参数和 T&amp;&amp; 参数`,但左值仍然只绑定到前一个重载......)。

【问题讨论】:

  • O.T.:有趣,我曾经写过similar sample 供我自己使用(有疑问时回想一下)。我不确定在生产性工作中是更喜欢“const ref”/“RValue ref”对还是模板。但是,还有一个本质的区别:模板化的版本可以接受除std::string之外的更多类型(你得考虑你喜欢还是不喜欢)。
  • @Scheff,是的,但仍然可以坚持使用模板版本并通过 SFINAE 或正文中的 static_assert 限制其域,以便它只接受 std::string 和/或任何其他需要的类型。
  • 勘误表的意思是,在第一个示例中,如果将newName 传递给另一个函数,它将始终选择const 重载。在第二个例子中,如果传入的参数本身不是const,那么将优先选择非const重载而不是const重载。这种差异的关键在于,这将是一种无声的差异
  • @MichaelBurr,是的,但是当我写这个问题时,你评论中的第一个 if 让我感到困惑。 如果 addName 的主体有newName.someMethod()/someFun(newName) 之类的东西,那么在第一个例子中std::string::someMethod() const {/someFun(const std::string&amp;) 将始终被选中,而在第二个例子中std::string::someMethod() {如果参数是非const左值,则可以选择/someFun(std::string&amp;)

标签: c++ c++11 overloading forwarding-reference effective-c++


【解决方案1】:

在第二种情况下,当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); }

这意味着std::vector&lt;&gt;::push_back 的非const 版本被调用

在第一种情况下,当 std::string 左值传递给 addName 时,选择第一个重载不管const-ness

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

这意味着在这两种情况下都选择了std::vector&lt;&gt;::push_backconst 重载。

【讨论】:

    猜你喜欢
    • 2014-12-04
    • 1970-01-01
    • 2023-03-03
    • 2011-09-12
    • 2014-08-27
    • 2022-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多