Analysis
Code ▼
#include <iostream> #include <string> class Foo { public: std::string bar; Foo(const std::string& d) : bar(d) { std::cout << "Constructor: " << bar << "\n"; } Foo(const Foo& other) : bar(other.bar) { std::cout << "Copy Constructor: " << bar << "\n"; } Foo(Foo&& other) noexcept : bar(std::move(other.bar)) { std::cout << "Move Constructor: " << bar << "\n"; } Foo& operator=(const Foo& other) { std::cout << "Copy Assignment: " << other.bar << "\n"; bar = other.bar; return *this; } Foo& operator=(Foo&& other) noexcept { std::cout << "Move Assignment: " << other.bar << "\n"; bar = std::move(other.bar); return *this; } void print() const { // std::cout << "Current content: " << bar << "\n"; } }; Foo g_foo("bar_0"); void copy_no_store(Foo foo) { foo.print(); } void const_ref_no_store(const Foo& foo) { foo.print(); } void ref_no_store(Foo& foo) { foo.print(); } template <typename T> void uni_ref_no_store(T&& foo) { foo.print(); } template <typename T> void const_uni_ref_no_store(const T&& foo) { foo.print(); } void copy_store(Foo foo) { g_foo = std::move(foo); } void const_ref_store(const Foo& foo) { g_foo = foo; } void ref_store(Foo& foo) { g_foo = foo; } template <typename T> void uni_ref_store(T&& foo) { g_foo = std::forward<T>(foo); } template <typename T> void const_uni_ref_store(const T&& foo) { g_foo = std::forward<const T>(foo); } void run_copy_no_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_1"); std::cout << "=== lvalue ===\n"; copy_no_store(foo); std::cout << "=== rvalue ===\n"; copy_no_store(std::move(foo)); } void run_const_ref_no_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_2"); std::cout << "=== lvalue ===\n"; const_ref_no_store(foo); std::cout << "=== rvalue ===\n"; const_ref_no_store(std::move(foo)); } void run_ref_no_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_3"); std::cout << "=== lvalue ===\n"; ref_no_store(foo); std::cout << "=== rvalue ===\n"; //ref_no_store(std::move(foo)); std::cout << "not possible\n"; } void run_uni_ref_no_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_4"); std::cout << "=== lvalue ===\n"; uni_ref_no_store(foo); std::cout << "=== rvalue ===\n"; uni_ref_no_store(std::move(foo)); } void run_const_uni_ref_no_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_5"); std::cout << "=== lvalue ===\n"; //const_uni_ref_no_store(foo); std::cout << "not possible\n"; std::cout << "=== rvalue ===\n"; const_uni_ref_no_store(std::move(foo)); } void run_copy_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_1"); std::cout << "=== lvalue ===\n"; copy_store(foo); std::cout << "=== rvalue ===\n"; copy_store(std::move(foo)); } void run_const_ref_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_2"); std::cout << "=== lvalue ===\n"; const_ref_store(foo); std::cout << "=== rvalue ===\n"; const_ref_store(std::move(foo)); } void run_ref_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_3"); std::cout << "=== lvalue ===\n"; ref_store(foo); std::cout << "=== rvalue ===\n"; //ref_store(std::move(foo)); std::cout << "not possible\n"; } void run_uni_ref_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_4"); std::cout << "=== lvalue ===\n"; uni_ref_store(foo); std::cout << "=== rvalue ===\n"; uni_ref_store(std::move(foo)); } void run_const_uni_ref_store() { std::cout << "\n" << __func__ << "\n"; Foo foo("bar_5"); std::cout << "=== lvalue ===\n"; //const_uni_ref_store(foo); std::cout << "not possible\n"; std::cout << "=== rvalue ===\n"; const_uni_ref_store(std::move(foo)); } int main() { std::cout << "\nNot store copy\n"; run_copy_no_store(); run_const_ref_no_store(); run_ref_no_store(); run_uni_ref_no_store(); run_const_uni_ref_no_store(); std::cout << "\nStore copy\n"; run_copy_store(); run_const_ref_store(); run_ref_store(); run_uni_ref_store(); run_const_uni_ref_store(); return 0; }
Output ▼
Constructor: bar_0 Not store copy run_copy_no_store Constructor: bar_1 === lvalue === Copy Constructor: bar_1 === rvalue === Move Constructor: bar_1 run_const_ref_no_store Constructor: bar_2 === lvalue === === rvalue === run_ref_no_store Constructor: bar_3 === lvalue === === rvalue === not possible run_uni_ref_no_store Constructor: bar_4 === lvalue === === rvalue === run_const_uni_ref_no_store Constructor: bar_5 === lvalue === not possible === rvalue === Store copy run_copy_store Constructor: bar_1 === lvalue === Copy Constructor: bar_1 Move Assignment: bar_1 === rvalue === Move Constructor: bar_1 Move Assignment: bar_1 run_const_ref_store Constructor: bar_2 === lvalue === Copy Assignment: bar_2 === rvalue === Copy Assignment: bar_2 run_ref_store Constructor: bar_3 === lvalue === Copy Assignment: bar_3 === rvalue === not possible run_uni_ref_store Constructor: bar_4 === lvalue === Copy Assignment: bar_4 === rvalue === Move Assignment: bar_4 run_const_uni_ref_store Constructor: bar_5 === lvalue === not possible === rvalue === Copy Assignment: bar_5
Not store copy
copy_ |
const_ref_ |
ref_ |
uni_ref_ |
const_uni_ref_ |
|
---|---|---|---|---|---|
lvalue | 1 Copy | – | – | – | Not possible |
rvalue | 1 Move | – | Not possible | – | – |
Store copy
copy_ |
const_ref_ |
ref_ |
uni_ref_ |
const_uni_ref_ |
|
---|---|---|---|---|---|
lvalue | 1 Copy 1 Move |
1 Copy | 1 Copy | 1 Copy | Not possible |
rvalue | 2 Moves | 1 Copy | Not possible | 1 Move | 1 Copy |
Result
Decision table
Small object? | Modify original? | Store copy? | Result |
---|---|---|---|
Yes | Yes | Yes | uni_ref_store |
Yes | Yes | No | uni_ref_no_store |
Yes | No | Yes | copy_store |
Yes | No | No | copy_no_store |
No | Yes | Yes | uni_ref_store |
No | Yes | No | uni_ref_no_store |
No | No | Yes | copy_store |
No | No | No | const_ref_no_store |
Further information: When and how to pass smart pointer (C++)