首页 > 解决方案 > 移动语义与 const 引用

问题描述

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

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

MyClass::MyClass(const std::string &title){
  this->title = title
}

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

MyClass::MyClass(std::string title){
  this->title = std::move(title)
}

所以我想知道在现代 C++ 中执行此操作的正确方法是什么。

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

标签: c++stringreferencemove-semantics

解决方案


None 是最佳的,因为它们都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

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


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

MyClass::MyClass(const std::string& title) : title(title) {}

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

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

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

MyClass(std::string title) : title(std::move(title)) {}

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

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

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

MyClass(const std::string& title) : title(title) {}
MyClass(std::string&& title) : title(std::move(title)) {}

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

  • 该成员是复制构造的。
  • 该成员是移动构造的。
  • Astd::stringstd::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

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

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

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

template<typename... Args>
explicit MyClass(Args&&... args) : title(std::forward<Args>(args)...) {}

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

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

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

template<typename T>
explicit MyClass(T&& title) : title(std::forward<T>(title)) {}

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

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

#6如果您想在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))
{}

推荐阅读