Last active
May 20, 2022 15:29
-
-
Save nthery/589885de47b41af177bfd2c6c22e729d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <utility> | |
struct Bar { | |
int *payload_; | |
Bar(); | |
~Bar(); | |
Bar(const Bar&); | |
Bar(Bar&&) noexcept; | |
Bar& operator=(const Bar&); | |
Bar& operator=(Bar&&) noexcept; | |
}; | |
////////////////////////////////////////////////////////////////////////////// | |
// RVALUES AND LVALUES | |
// DISCLAIMER: WHAT FOLLOWS CONTAINS LIES BY OMISSION AND APPROXIMATION! | |
// For the purpose of this talk: | |
// lvalue: a named object used in an expression | |
Bar global_bar; | |
// rvalue: a call to a function returning by value | |
Bar build_bar(); | |
////////////////////////////////////////////////////////////////////////////// | |
// REFERENCE FLAVORS | |
// References come in several flavors | |
// - non-const lvalue ref | |
// - const lvalue ref | |
// - rvalue ref | |
// - forwarding (aka universal) ref | |
// A non-const lvalue ref binds to lvalues only. | |
void test_non_const_lvalue_ref() { | |
[[maybe_unused]] Bar& r1 = global_bar; | |
// Bar& r2 = build_bar(); // compile error | |
} | |
// A const lvalue ref binds to lvalues and rvalues. | |
void test_const_lvalue_ref() { | |
[[maybe_unused]] Bar& r1 = global_bar; | |
[[maybe_unused]] const Bar& r3 = build_bar(); | |
} | |
// An rvalue ref binds to rvalues only. | |
// Motivation for rvalues: move semantics | |
void test_rvalue_ref() { | |
// Bar&& r2 = global_bar; // compile error | |
[[maybe_unused]] Bar&& r1 = build_bar(); | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
// MOTIVATION FOR PERFECT FORWARDING | |
// perfect forwarding == forward without unneeded copies | |
void target(const Bar& x); | |
void target(Bar&& x); | |
// Let's use a const ref as they bind to both lvalues and rvalues. | |
template<class T> | |
void imperfect_forwarder(const T& x) { | |
// ... | |
target(x); | |
// ... | |
} | |
void test_imperfect_forwarder() { | |
// This calls target(const Bar&) :-) | |
imperfect_forwarder(global_bar); | |
// This calls target(const Bar&) which may cause a useless copy :-( | |
imperfect_forwarder(build_bar()); | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
// FORWARDING REFERENCES AND PERFECT FORWARDING | |
// A forwarding (aka universal) reference: | |
// - Preserves rvalues and lvalues | |
// - Looks syntactically like an rvalue ref but must follow the exact form below: | |
// - Parameter type is `T&&` (or `Ts...&&`) | |
// - T is a template parameter of the function | |
template<class T> | |
void perfect_forwarder(T&& x) { | |
// ... | |
target(std::forward<T>(x)); | |
// ... | |
} | |
void test_perfect_fwd() { | |
// This calls target(const Bar&) :-) | |
perfect_forwarder(global_bar); | |
// This calls target(Bar&&), no redundant copies :-) | |
perfect_forwarder(build_bar()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment