2

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

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
    ..
    ...


}

有了右值引用,像这样提供另一个成员函数会更好吗

class X
{
...
...
....
 void set_data(string s)
 {
     internal = std::move(s);
 }
};

这将允许此类的客户端使用移动语义并防止每次使用一组分配/复制操作。这是一个高度编造的示例,但相同的原则是否适用于所有类设计,而不会破坏“最小接口”范式。

非常感谢任何人对此事的见解?

4

2 回答 2

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

这大致相当于您正确建议的单个重载。两次重载解决方案的性能优势非常小。string当传递的参数是一个 xvalue(一个已用 转换的左值)时,单重载解决方案将花费额外的移动构造std::move。但是移动构造函数std::string应该非常快,所以这应该没什么大不了的。我只是本着充分披露的精神提及它。

如果set_data有多个参数,“按值”方法变得更具吸引力。例如考虑需要传入两个strings 的情况。您的选择是:

解决方案 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;}

永远不要用引用重载按值,无论是左值引用还是右值引用。

于 2012-11-16T22:41:17.143 回答
-1

我认为没有理由将两者都void set_data(const char* s)作为void set_data(string s)界面的一部分。这将产生歧义并且容易产生副作用。此外,您仍然在 call to 中按值传递参数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.

如果您还想有一个按值选项,您可以使用rvalue此 API 的版本与字符串复制构造函数:set_data(string(str)).

于 2012-11-16T09:30:41.400 回答