How to pass arguments (C++)

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_no_store const_ref_no_store ref_no_store uni_ref_no_store const_uni_ref_no_store
lvalue 1 Copy Not possible
rvalue 1 Move Not possible

Store copy

copy_store const_ref_store ref_store uni_ref_store const_uni_ref_store
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++)