移动语义与常量引用

Fin*_*inn 5 c++ string reference move-semantics

我的类有字符串变量,我想用传递给构造函数的值来初始化它们。

我的老师认为我们将字符串作为常量引用传递:

MyClass::MyClass(const std::string &title){
  this->title = title
}
Run Code Online (Sandbox Code Playgroud)

但是 Clang-Tidy 建议使用 move 命令:

MyClass::MyClass(std::string title){
  this->title = std::move(title)
}
Run Code Online (Sandbox Code Playgroud)

所以我想知道在现代 C++ 中这样做的正确方法是什么。

我已经环顾四周,但没有什么能真正回答我的问题。提前致谢!

Ted*_*gmo 7

没有一个是最佳的,因为它们都title首先默认构造,然后复制分配移动分配它。使用成员初始值设定项列表。

MyClass::MyClass(const std::string& title) : title(title) {}         // #1
// or
MyClass::MyClass(std::string title) : title(std::move(title)) {}     // #2
//or
MyClass::MyClass(const std::string& title) : title(title) {}         // #3
MyClass::MyClass(std::string&& title) : title(std::move(title)) {}   // #3
Run Code Online (Sandbox Code Playgroud)

让我们看看它们,看看 C++17 中会发生什么:


#1 - 单个转换构造函数采用const&.

MyClass::MyClass(const std::string& title) : title(title) {}
Run Code Online (Sandbox Code Playgroud)

这将std::string通过以下方式之一创建 1 或 2 s:

  • 该成员是复制构造的。
  • Astd::string由转换构造函数构造std::string,然后复制构造成员。

#2 - 单个转换构造函数采用std::string按值。

MyClass(std::string title) : title(std::move(title)) {}
Run Code Online (Sandbox Code Playgroud)

这将std::string通过以下方式之一创建 1 或 2 s:

  • 参数是通过临时 ( + ) 的返回值优化来构造的,然后移动构造成员。str1str2
  • 参数是复制构造的,然后成员是移动构造的。
  • 参数被移动构造,然后成员被移动构造。
  • 参数由std::string转换构造函数构造,然后移动构造成员。

#3 - 组合两个转换构造函数。

MyClass(const std::string& title) : title(title) {}
MyClass(std::string&& title) : title(std::move(title)) {}
Run Code Online (Sandbox Code Playgroud)

这将std::string通过以下方式之一创建 1 或 2 s:

  • 该成员是复制构造的。
  • 该成员是移动构建的。
  • Astd::string由转换构造函数构造std::string,然后移动构造成员。

到目前为止,选项#3似乎是最有效的选择。让我们再检查几个选项。


#4 - 与 #3 类似,但用转发构造函数替换移动转换构造函数。

MyClass(const std::string& title) : title(title) {}                       // A
template<typename... Args>
explicit MyClass(Args&&... args) : title(std::forward<Args>(args)...) {}  // B
Run Code Online (Sandbox Code Playgroud)

这将始终std::string以下列方式之一创建 1:

  • 该成员是通过 复制构造的A
  • 该成员是通过 移动构造的B
  • 该成员由std::string(可能转换的)构造函数通过构造B

#5 - 仅转发构造函数 - 从 #4 中删除复制转换构造函数。

template<typename... Args>
explicit MyClass(Args&&... args) : title(std::forward<Args>(args)...) {}
Run Code Online (Sandbox Code Playgroud)

这将始终std::string像#4 中那样创建 1,但所有操作都是通过转发构造函数完成的。

  • 该成员是复制构造的。
  • 该成员是移动构建的。
  • 该成员由(可能是转换的)构造函数构造std::string

#6 - 单参数转发转换构造函数。

template<typename T>
explicit MyClass(T&& title) : title(std::forward<T>(title)) {}
Run Code Online (Sandbox Code Playgroud)

这将始终像 #4 和 #5 一样创建 1 std::string,但只会采用一个参数并将其转发给std::string构造函数。

  • 该成员是复制构造的。
  • 该成员是移动构建的。
  • 该成员由转换构造函数构造std::string

#6如果您想在构造函数中采用多个参数,则可以轻松地使用Option来进行完美转发MyClass。假设您有一个int成员和另一个std::string成员:

template<typename T, typename U>
MyClass(int X, T&& title, U&& title2) :
    x(X),
    title(std::forward<T>(title)),
    title2(std::forward<U>(title2))
{}
Run Code Online (Sandbox Code Playgroud)