我应该在构造函数中总是使用std::move吗?

8

在示例A中使用的移动语义是否必要,哪个结构体更优?

示例A:

struct A
{
    std::string a;
    A( std::string a ) : a( std::move(a) ){ }
};

例子 B:

struct B
{
    std::string b;
    B( const std::string& b ) : b( b ){ }
};

我不认为这是一个重复的问题。我特别询问从使用类构造函数中的成员初始化的角度来看哪个示例更好。在其他问题中列出的示例或答案都没有涉及成员初始化。
我不喜欢构造函数使用引用参数,然后将其复制到成员中。这似乎会浪费多次复制操作。
我希望以尽可能高效的方式将数据“导入”成员,但我不想将rvalue作为构造函数参数。

3
如果值是 int 或其他单值基本类型,使用 move 或引用(无论是 const 还是其他方式)都是无意义的。此外,考虑在这种情况下不使用显式构造函数的结构体,因为聚合初始化在这里完全可以胜任。 - ShadowRanger
我在示例中使用了 int 来简化。如果使用 std::string 或其他更复杂的类,哪个更优?(我修改了我的答案并将 int 更改为 std::string - Dustin Nieffenegger
1
我不认为这是一个重复的问题。没有任何答案或问题涉及成员初始化。如果您看一下示例A,它会在参数中创建一个副本,然后将其移动到成员中。在示例B中,它创建了一个引用,然后将其复制到成员中。因此,示例B将有两个复制操作,而示例A只有一个,对吗? - Dustin Nieffenegger
1
你有第三种选择,提供 A(std::string&&) - Jarod42
似乎进行多次复制操作可能会造成浪费。在第二种情况下,我计算了只有一次复制。第一种情况才是浪费的,因为它将执行复制构造函数+移动构造函数,从本质上讲是一种复制。 - ComicSansMS
显示剩余2条评论
2个回答

4

结构体 A 更优秀。

移动对象通常非常便宜(通常可以完全优化掉),因此通常不必关心移动次数。但最重要的是要尽量减少复制的次数。在示例A中,复制次数等于或少于示例B中的复制次数。

更具体地说,如果原始字符串为L值,则A和B等效:

std::string s;
...
A a(s);  // one copy
B b(s);  // one copy

但当原始字符串是R值时,A更好:

std::string MakeString();
...
A a(MakeString());  // zero copies
B b(MakeString());  // one copy

1

在我看来,将两者进行比较是不恰当的。

大多数情况下,实现的复制构造函数会进行深度复制,其主要目的是确保源对象不被修改

std::move最终为右值引用调用移动构造函数,只是复制指针并且可以将源对象指针设置为NULL。这种情况主要用于临时对象

因此,这两个例子是为了不同的目的而设计的一个(复制构造函数)用于保持源对象不变,而另一个(std::move)则用于处理临时对象。


4
std::move的情况下,参数按值传递,因此使用std::string的复制构造函数创建一个临时对象,然后将其移动到成员变量中——据我所知,这两种情况是等效的,选择哪种只是一种风格问题。 - Christopher Oicles
难道示例A不更好一些吗?因为它没有使用引用作为中介来创建副本。 - Dustin Nieffenegger
例子A是否也有复制构造函数的实现?即使没有,当您按值传递时,也会调用默认的复制构造函数。因此,在例子A中,不是像“完整的复制构造函数调用+std::move”吗? - Pavan Chandaka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接