Reputation: 367
I've been reading up on copy and move semantics to improve my knowledge of when to use T
vs T const &
vs T &&
I wrote a small bit of test code to verify my intuition
#include <iostream>
#include <utility>
#include <vector>
template< int i >
struct Example
{
Example(){ std::cout << "("<< i<<") was made\n"; }
Example( Example const & ){ std::cout << "("<< i<<") was copied\n"; }
Example( Example && ){ std::cout << "("<< i<<") was moved\n"; }
~Example(){ std::cout << "("<< i<<") was destroyed\n"; }
std::vector< int > x = {1,2,3,4};
};
template< int i >
struct Test
{
Example< i > example;
// Test( Example< i > example ): example( example ) {}
// Test( Example< i > const & example ): example( example ) {}
// Test( Example< i > && example ): example( std::move(example) ) {}
};
int main()
{
Example<1> example1;
Example<2> example2;
std::cout << "Test1\n";
Test test1{ example1 };
std::cout << "Test2\n";
Test test2{ std::move(example2) };
std::cout << "Test3\n";
Test test3{ Example<3>() };
std::cout << "Tests done \n";
}
This produces the output I expect:
(1) was made
(2) was made
Test1
(1) was copied
Test2
(2) was moved
Test3
(3) was made
Tests done
(3) was destroyed
(2) was destroyed
(1) was destroyed
(2) was destroyed
(1) was destroyed
g++ -std=c++20 -O3
Adding explicit constructor to Test
Test( Example< i > const & example ): example( example ) {}
Test( Example< i > && example ): example( std::move( example ) ) {}
Still fits my intuition, as now that there is only a copy and move, test3
now resorts to using a move.
However when only defining the following constructor
Test( Example< i > example ): example( example ) {}
I get
(1) was made
(2) was made
Test1
(1) was copied
(1) was copied
(1) was destroyed
Test2
(2) was moved
(2) was copied
(2) was destroyed
Test3
(3) was made
(3) was copied
(3) was destroyed
Tests done
(3) was destroyed
(2) was destroyed
(1) was destroyed
(2) was destroyed
(1) was destroyed
Whilst I can understand the extra copy of example1
, it still seems strange to me. However it does not make sense in combination with what happends to example3
I would either also expect two copies or a move.
I was always under the impression that:
const &
) or if you plan on setting the value.Put with this example this does not seem the case, as given in the example Example
contains a vector, thus a copy is not the lightest operation there is and a move would be prefereable.
So my question summarized is:
To avoid expensive copies and to allow for constructing in place, when should you use T
, T&
, T const &
and T&&
( maybe T const &&
).
This is also in the context of functions not just constructors.
( Note: with construct in place I mean the phenomina as shown for test3
initialy where the temporary object is neither moved or copied into test3
)
Upvotes: -4
Views: 83
Reputation: 153
In short, prefer reference T&
, unless the type is small, like int
. If the type is not going to be used by the caller code, consider T&& + std::move().
The simplicity of the code is more important that speed, unless you measure that this is a hot spot of your program.
C++ Core guidelines that you linked in the comment: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#fcall-parameter-passing
Upvotes: 2