modernize-pass-by-value

随着移动语义的加入,以及标准库通过为许多类型添加移动构造函数而更新,直接按值传递参数(而不是按常量引用传递,然后复制)变得更加有趣。此检查允许编译器负责选择最佳的构造方式。

当调用代码传递一个 *右值* 并假设移动构造是一个廉价操作时,这种转换通常是有益的。以下简短的例子说明了值的构造是如何发生的

void foo(std::string s);
std::string get_str();

void f(const std::string &str) {
  foo(str);       // lvalue  -> copy construction
  foo(get_str()); // prvalue -> move construction
}

注意

目前,只有构造函数被转换为使用按值传递。欢迎为其他情况提供贡献!

构造函数中的按值传递

替换使用按常量引用传递的构造函数参数,这些参数被复制到类字段中。然后使用 `std::move()` 移动参数。

由于 `std::move()` 是在 `` 中声明的库函数,因此可能需要添加此包含。检查将根据需要添加包含指令。

 #include <string>

 class Foo {
 public:
-  Foo(const std::string &Copied, const std::string &ReadOnly)
-    : Copied(Copied), ReadOnly(ReadOnly)
+  Foo(std::string Copied, const std::string &ReadOnly)
+    : Copied(std::move(Copied)), ReadOnly(ReadOnly)
   {}

 private:
   std::string Copied;
   const std::string &ReadOnly;
 };

 std::string get_cwd();

 void f(const std::string &Path) {
   // The parameter corresponding to 'get_cwd()' is move-constructed. By
   // using pass-by-value in the Foo constructor we managed to avoid a
   // copy-construction.
   Foo foo(get_cwd(), Path);
 }

如果参数被使用多次,则不执行转换,因为移动的对象具有未定义的状态。这意味着以下代码将保持不变

#include <string>

void pass(const std::string &S);

struct Foo {
  Foo(const std::string &S) : Str(S) {
    pass(S);
  }

  std::string Str;
};

已知限制

生成的代码可能出现错误的一种情况是,引用对象在初始化列表中通过“隐藏”引用被修改,然后赋值。

示例

 std::string s("foo");

 struct Base {
   Base() {
     s = "bar";
   }
 };

 struct Derived : Base {
-  Derived(const std::string &S) : Field(S)
+  Derived(std::string S) : Field(std::move(S))
   { }

   std::string Field;
 };

 void f() {
-  Derived d(s); // d.Field holds "bar"
+  Derived d(s); // d.Field holds "foo"
 }

有关延迟模板解析的说明

当启用延迟模板解析时,模板化上下文中的一部分构造函数;模板化构造函数、类模板中的构造函数、模板类内部类的构造函数等,不会被转换。延迟模板解析在 Windows 上默认启用,作为 Microsoft 的扩展:Clang 编译器用户手册 - Microsoft 扩展.

可以使用 `-fdelayed-template-parsing` 标志启用延迟模板解析,使用 `-fno-delayed-template-parsing` 标志禁用。

示例

  template <typename T> class C {
    std::string S;

  public:
=  // using -fdelayed-template-parsing (default on Windows)
=  C(const std::string &S) : S(S) {}

+  // using -fno-delayed-template-parsing (default on non-Windows systems)
+  C(std::string S) : S(std::move(S)) {}
  };

另见

有关按值传递习语的更多信息,请阅读:想要速度?按值传递.

选项

IncludeStyle

一个字符串,指定使用哪种包含风格,`llvm` 或 `google`。默认值为 `llvm`。

ValuesOnly

当为 `true` 时,检查仅警告已经按值传递的已复制参数。默认值为 `false`。