#include #include #include #include #include #include using namespace fmt; // Some heavy object struct A { A() = default; A(int val) : val(val) { std::cout << "called constructor with: " << val << std::endl; } A(A const& other) { std::cout << "calling copy(A const& other): " << val << std::endl;; } A(A&& other) { std::cout << "calling move(A&& other): " << val << std::endl;; } A& operator=(A&& other) { std::cout << "calling move(A&& other)=: " << val << std::endl;; return *this; } A& operator=(A const& other) { std::cout << "calling copy(A const& other)=: " << val << std::endl;; return *this; } ~A() = default; int val{}; }; template void print_helper(std::string_view var, T&&) { std::string name = typeid(T).name(); if constexpr (std::is_const_v) { name += " const"; } if constexpr (std::is_lvalue_reference_v) { name += "&"; } else { name += "&&"; } std::cout << "Type of '" << var << "': '" << name <<"'\n"; } #define print_t(VAR) print_helper(#VAR, VAR) A rvo_impl_1() { A a; std::cout << "rvo_impl_1\n"; a.val = rand() % 2; return a; } // calls the move constructor or it might optimize away it in -O3 std::string rvo_impl_2(int v) { std::string res; std::cout << "rvo_impl_2\n"; res = std::to_string(v); return res; } // It'll call copy constructors std::string rvo_not_work_complex(int v) { if (v % 5 == 0) return "multiple of five"; if (v % 2 == 0) return "multiple of 2"; return std::to_string(v); } // call move constructor A rvo_not_work_impl(int v) { std::cout << "rvo_not_work_impl: "; A a; if (v % 2) { a.val = v % 2; } else { // returning early // you might think who would do that let me show you an example return {}; } return a; } // copy-elision/RVO does not call any constructor since they get optimized away A copy_elision() { std::cout << "copy_elision\n"; return A(); // pr-value } // RVO(return value optimization) is a part of copy-elision, but "copy-elision" is // a much wider concept that applies to other parts of the program. It removes // copy/move constuctors // What is l-value? // l-values are references to the objects. These are objects you can hold onto // like constructed objects, variables or references. // example: // 1. int& // 2. int const& // 3. int a = 4; foo(a <-- this will be a l-value) // What is r-value? // r-values are the values that is temporary. // example: // 1. literals like "hello", 1, 2.5 // 2. call to object constructor like A(), std::string("Hello"), ... // 3. A&&, int&&, ... these are r-values that you've seen inside the move constructors // There are more value that you can look into "pr-value", ... // https://en.cppreference.com/w/cpp/language/value_category // You may be thinking why i'm talking about them. The reason being these concept // allows you understand when to use "std::move" and when it's useless. For that // let me implement my version of move constructor template // decltype(auto) means forward the return type or whatever I'm returning infer the type with // "const&", "&" or "&&" decltype(auto) my_move(T&& val) { // here T&& is not a r-value. It's perfect forwarding. Whenever you see "&&" you see // with template it means perfect forwading. I know cpp has fucked-up syntax. using type = std::decay_t; // all this does is cast l-value to r-value return static_cast(val); } TEST_CASE("RVO tests", "[base]") { A a{10}; int argc; std::cout << "\n\n######### RVO TESTS ###############\n"; auto t0 = rvo_impl_1(); // no constructor will be called because of return value optimization auto t1 = rvo_not_work_impl(argc); auto t2 = copy_elision(); auto t3 = rvo_not_work_complex(argc); auto t4 = rvo_impl_2(argc); print_t(a); // pass by l-value | output: Type of 'a': 'A&' print_t(std::move(a)); // pass by r-value | output: Type of 'std::move(a)': 'A&&' print_t(my_move(a)); // pass by r-value | output: Type of 'my_move(a)': 'A&&' print_t(A{}); // pass by r-value | output: Type of 'A{}': 'A&&' static_assert(!std::is_rvalue_reference_v, "'a' is l-value"); // you can remove the negation and see it failing static_assert(!std::is_lvalue_reference_v, "This should pass if I've implement it correctly"); }