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++)