Rvalue引用和类设计

时间:2012-10-17 01:49:12

标签: c++ interface c++11 rvalue-reference

我试图评估右值引用如何影响类的设计。假设我有一个现有的课程,如下所示

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);
 }
};

这将允许此类的客户端使用移动语义并防止每次使用一组分配/复制操作。这是一个高度炮制的例子,但同样的原则适用于所有类设计而不打破“最小界面”范例。

非常感谢有关此事的任何见解?

2 个答案:

答案 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))