We are playing around with clang-tidy on our builds and I have come across something that I don't fully understand about it's behavior. I will put the code first and my questions afterwards.
If I use the following code:
#include <iostream>
using namespace std;
class Param {
public:
Param() {cout << "constructor" << endl;}
~Param() {cout << "destructor" << endl;}
Param(const Param &other) {cout << "copy constructor" << endl;}
Param(Param&& other) {cout << "move constructor" << endl;}
Param& operator=(const Param& other) {cout << "copy assignment" << endl; return *this; }
Param& operator=(Param&& other) {cout << "Param move assignment" << endl; return *this; }
};
class A {
public:
A(const Param& param) : m_param(param){}
Param m_param;
};
int main() {
const Param param;
A a(param);
}
and this CMake file:
cmake_minimum_required(VERSION 3.2)
project(clang)
add_executable(clang main.cpp)
set_property(TARGET clang PROPERTY CXX_STANDARD 14)
the generated binary will output:
constructor
copy constructor
destructor
destructor
If I then enable clang-tidy on the CMake file like so:
cmake_minimum_required(VERSION 3.2)
project(clang)
add_executable(clang main.cpp)
set_property(TARGET clang PROPERTY CXX_STANDARD 14)
set_property(TARGET clang PROPERTY CXX_CLANG_TIDY "/usr/bin/clang-tidy;-checks=modernize-*;-fix")
compilation will print the following:
/.../main.cpp:17:7: warning: pass by value and use std::move [modernize-pass-by-value]
A(const Param& param) : m_param(param){}
^
/.../main.cpp:2:1: note: FIX-IT applied suggested code changes
^
/.../main.cpp:17:7: note: FIX-IT applied suggested code changes
A(const Param& param) : m_param(param){}
^
/.../main.cpp:17:37: note: FIX-IT applied suggested code changes
A(const Param& param) : m_param(param){}
^
/.../main.cpp:17:42: note: FIX-IT applied suggested code changes
A(const Param& param) : m_param(param){}
^
and code gets modified to:
class A {
public:
A(Param param) : m_param(move(param)){}
Param m_param;
};
if I then run the binary I get:
constructor
copy constructor
move constructor
destructor
destructor
destructor
Questions:
1 - In both situations the copy constructor is needed:
- copying to the member variable
- copying in the constructor argument
But why does clang-tidy "thinks" that the extra move constructor actually helps? doesn't that only add extra computational cost?
http://clang.llvm.org/extra/clang-tidy/checks/modernize-pass-by-value.html
Documentation says that it assumes that move is cheap (which makes sense), but aren't you adding that to the copy anyway?
EDIT: seems like this question got answered here. I am still curious about question 2.
2 - Assuming that I misunderstood something and clang-tidy is correct, what should one do when subclassing? pass by value twice?
class A {
public:
A(Param param) : m_param(move(param)){}
Param m_param;
};
class B : public A {
public:
B(Param param) : A(move(param)){}
};