我试图评估右值引用如何影响类的设计。假设我有一个现有的课程,如下所示
class X
{
string internal;
public:
void set_data(const char* s)
{
internal = s;
}
..
..
..
//other stuff
};
此类由另一个模块使用:
//another module
{
string configvalue;
X x;
//read configvalue from a file and call set
...
x.set_data(configvalue.c_str());
//use x to do some magic
..
...
}
使用rvalue引用会更好地提供另一个成员函数,如此
class X
{
...
...
....
void set_data(string s)
{
internal = std::move(s);
}
};
这将允许此类的客户端使用移动语义并防止每次使用一组分配/复制操作。这是一个高度炮制的例子,但同样的原则适用于所有类设计而不打破“最小界面”范例。
非常感谢有关此事的任何见解?
答案 0 :(得分:4)
是的,按照您的建议添加string
重载是一个好主意。即使没有右值引用,这样的过载也是个好主意。否则,给定std::string s
,使用它必须:
x.set_data(s.c_str());
,而
x.set_data(s);
对于X
的客户来说,更直观(甚至更有效率)。
作为另一种选择,您可以添加这两个重载:
void set_data(const string& s) {internal = s;}
void set_data(string&& s) {internal = std::move(s);}
这大致相当于您正确建议的单一过载。双重过载解决方案具有非常轻微的性能优势。当传递的参数是xvalue(已使用string
强制转换的左值)时,单重载解决方案将花费额外的std::move
移动构造。但是std::string
的移动构造函数应该非常快,所以这不应该是一个大问题。我只是本着充分披露的精神提到它。
如果set_data
有多个参数,则“按值”方法会变得更具吸引力。例如,考虑您需要传递两个string
的情况。您的选择是:
解决方案1
void set_data(string s1, string s2);
解决方案2
void set_data(const string& s1, const string& s2);
void set_data( string&& s1, const string& s2);
void set_data(const string& s1, string&& s2);
void set_data( string&& s1, string&& s2);
正如您可以快速看到的那样,解决方案2的参数数量很难缩放。
最后,在任何情况下都不应该尝试将两种解决方案应用于同一类型:
不要这样做!
void set_data(string s) {internal = std::move(s);}
void set_data(const string& s) {internal = s;}
void set_data(string&& s) {internal = std::move(s);}
这组重载将是模糊的。就像在C ++ 03中一样,以下两个重载是不明确的:
void set_data(string s) {internal = std::move(s);}
void set_data(const string& s) {internal = s;}
永远不要使用引用来重载by-value,无论是左值引用还是右值引用。
答案 1 :(得分:-1)
我认为没有理由同时将void set_data(const char* s)
和void set_data(string s)
作为界面的一部分。这会产生歧义,并且容易产生副作用。此外,您仍然可以在调用set_data(string s)
时按值传递参数。相反,我建议定义以下2个函数:
void set_data(const string &s);
void set_data(string &&s);
这样你可以有2个实现,首先是深层复制你的字符串,第二个可以窃取字符串的内部,因为它是一个rvalue
(确保将它保持在一个已定义的状态,所以析构函数将是能够毫无问题地销毁它 - 有关详细信息,请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics)。
第二个版本将在rvalue
string
参数上自动调用,或者如果参数被强制为rvalue
,例如std::move
。
如果您还希望使用按值选项,则可以使用此API的rvalue
版本以及字符串复制构造函数:set_data(string(str))
。