【发布时间】: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<T>::push_back 有两个重载 const T& 参数和 T&& 参数`,但左值仍然只绑定到前一个重载......)。
【问题讨论】:
-
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&)将始终被选中,而在第二个例子中std::string::someMethod() {如果参数是非const左值,则可以选择/someFun(std::string&)。
标签: c++ c++11 overloading forwarding-reference effective-c++