Quick reference of C++ value categories: Part 2

by
https://habrastorage.org/webt/nh/rz/-s/nhrz-smfzjdftvbzeuurpb7tpsu.png

Go to Part 1

Passing parameters to a function

When passing parameter to a function, category of a passed expression is implicitly converted to the category of function parameter: void f(TO_TYPE p); FROM_TYPE x; f(x); This implicit conversion takes place the same way as during an assignment (see "Assignment" section above) except that function definition cannot contain "auto" types.

https://habrastorage.org/webt/o5/e9/c1/o5e9c1gvz6zisyjjkjvvu7elmsu.png

When function has multiple overloads with same number and types of parameters but different categories of parameters, overloading mechanism selects the most applicable function overload based on the category. In the table below function overloads are assigned an order in which each overload is selected when passing an expression of a particular category (e.g. 1 means that this overload will always be selected first if available):

https://habrastorage.org/webt/gk/e9/6t/gke96tfgahczmsch_fihgqmf0tw.png

Footnotes:

1 — a temporary created to hold a reference initializer persists until the end of function scope.

Note:

Examples and tests of variants with printing of each called constructor and operator:

Examples and tests with printing of each called constructor and operator
#include <iostream>
#include <iomanip>
#include <map>
#include <vector>
#include <string>

using namespace std;

template<class C, class T>
auto contains(const C& v, const T& x)
-> decltype(end(v), true)
{
return end(v) != std::find(begin(v), end(v), x);
}

template <class... Types>
constexpr inline __attribute__((__always_inline__)) int UNUSED(Types&&...) {
return 0;
};

map<string, map<string, string>> res;
vector<string> froms;
vector<string> tos;
string from;
string to;

int ready = 0;

void report(string st) {
if (!from.empty() && !to.empty()) {
res[from][to] += st;
}
if (ready) cout << st << " ";
}

struct T {
T() {
report("Dc");
}
T(int va) : a(va) {
report("Pc");
}
T(const T& other) :
a(other.a)
{
report("Cc");
}
T(T&& other) :
a(std::exchange(other.a, 0))
{
report("Mc");
}
T& operator=(int va) {
report("Va");
a = va;
return *this;
}
T& operator=(const T& rhs) {
report("Ca");
// check for self-assignment
if(&rhs == this) return *this;
a = rhs.a;
return *this;
}
T& operator=(T&& rhs) {
report("Ma");
// check for self-assignment
if(&rhs == this) return *this;
a = std::exchange(rhs.a, 0);
return *this;
}
~T() {
report("D");
}
int a = 1;
};

void func_start() {
cout << "|";
}

T Fprv() { return T(4); }
const T Fcprv() { return T(5); }
T gs;

void t(T s) { func_start(); cout << s.a; }
void ct(const T s) { func_start(); cout << s.a; }
void tr(T& s) { func_start(); cout << s.a; }
void ctr(const T& s) { func_start(); cout << s.a; }
void trr(T&& s) { func_start(); cout << s.a; }
void ctrr(const T&& s) { func_start(); cout << s.a; }
template<typename Z>
void pf(Z&& s) { func_start(); gs = forward<Z>(s); cout << gs.a; }

void print_col(const string &st, int width) {
cout << endl << left << setw(width) << st;
}

void test_pass(string lto, string lfrom) {
from = lfrom;
to = lto;
res[from][to] = "";
if (!from.empty() && !to.empty()) {
if (!contains(froms, from)) froms.push_back(from);
if (!contains(tos, to)) tos.push_back(to);
}
print_col(lto + "(" + lfrom + "): ", 20);
}

#define EVAL(x) #x

#define TEST_PASS(t, v) { \
test_pass(EVAL(t), #v); \
t(v); \
cout << "-"; \
}

void test_conversion() {
ready = 1;
T LV;
const T CLV;
T& LR = LV;
const T& CLR = LV;
//T&& XV = T(); -- actually LR
//const T&& CXV = T(); -- actually LR
auto &&fr = T();

#undef DT
#define DT t
TEST_PASS(DT, 2);
TEST_PASS(DT, Fprv());
TEST_PASS(DT, Fcprv());
TEST_PASS(DT, LV);
TEST_PASS(DT, CLV);
TEST_PASS(DT, LR);
TEST_PASS(DT, CLR);
TEST_PASS(DT, move(LV));
TEST_PASS(DT, move(CLV));
TEST_PASS(DT, fr);
#undef DT
#define DT ct
TEST_PASS(DT, 2);
TEST_PASS(DT, Fprv());
TEST_PASS(DT, Fcprv());
TEST_PASS(DT, LV);
TEST_PASS(DT, CLV);
TEST_PASS(DT, LR);
TEST_PASS(DT, CLR);
TEST_PASS(DT, move(LV));
TEST_PASS(DT, move(CLV));
TEST_PASS(DT, fr);
#undef DT
#define DT tr
//TEST_PASS(DT, 2);
//TEST_PASS(DT, Fprv());
//TEST_PASS(DT, Fcprv());
TEST_PASS(DT, LV);
//TEST_PASS(DT, CLV);
TEST_PASS(DT, LR);
//TEST_PASS(DT, CLR);
//TEST_PASS(DT, move(LV));
//TEST_PASS(DT, move(CLV));
TEST_PASS(DT, fr);
#undef DT
#define DT ctr
TEST_PASS(DT, 2);
TEST_PASS(DT, Fprv());
TEST_PASS(DT, Fcprv());
TEST_PASS(DT, LV);
TEST_PASS(DT, CLV);
TEST_PASS(DT, LR);
TEST_PASS(DT, CLR);
TEST_PASS(DT, move(LV));
TEST_PASS(DT, move(CLV));
TEST_PASS(DT, fr);
#undef DT
#define DT trr
TEST_PASS(DT, 2);
TEST_PASS(DT, Fprv());
//TEST_PASS(DT, Fcprv());
//TEST_PASS(DT, LV);
//TEST_PASS(DT, CLV);
//TEST_PASS(DT, LR);
//TEST_PASS(DT, CLR);
TEST_PASS(DT, move(LV));
//TEST_PASS(DT, move(CLV));
//TEST_PASS(DT, fr);
#undef DT
#define DT ctrr
TEST_PASS(DT, 2);
TEST_PASS(DT, Fprv());
TEST_PASS(DT, Fcprv());
//TEST_PASS(DT, LV);
//TEST_PASS(DT, CLV);
//TEST_PASS(DT, LR);
//TEST_PASS(DT, CLR);
TEST_PASS(DT, move(LV));
TEST_PASS(DT, move(CLV));
//TEST_PASS(DT, fr);
#undef DT
#define DT pf
TEST_PASS(DT, 2);
TEST_PASS(DT, Fprv());
TEST_PASS(DT, Fcprv());
TEST_PASS(DT, LV);
TEST_PASS(DT, CLV);
TEST_PASS(DT, LR);
TEST_PASS(DT, CLR);
TEST_PASS(DT, move(LV));
TEST_PASS(DT, move(CLV));
TEST_PASS(DT, fr);

cout << endl;
const int twidth = 10;
cout << left << setw(twidth) << "From:";
for (const auto& lto : tos) {
cout << left << setw(twidth) << lto;
}
cout << endl;
for (const auto& lfrom : froms) {
cout << left << setw(twidth) << lfrom;
for (const auto& lto : tos) {
if (!res.count(lfrom) || !res[lfrom].count(lto)) {
cout << left << setw(twidth) << "-";
} else if (res[lfrom][lto].empty()) {
cout << left << setw(twidth) << "+";
} else {
cout << left << setw(twidth) << res[lfrom][lto];
}
}
cout << endl;
}

cout << endl;
}

int main() {
test_conversion();
cout << endl;
return 0;
}

/* Output:

Dc Dc Dc
t(2): Pc |2D -
t(Fprv()): Pc |4D -
t(Fcprv()): Pc |5D -
t(LV): Cc |1D -
t(CLV): Cc |1D -
t(LR): Cc |1D -
t(CLR): Cc |1D -
t(move(LV)): Mc |1D -
t(move(CLV)): Cc |1D -
t(fr): Cc |1D -
ct(2): Pc |2D -
ct(Fprv()): Pc |4D -
ct(Fcprv()): Pc |5D -
ct(LV): Cc |0D -
ct(CLV): Cc |1D -
ct(LR): Cc |0D -
ct(CLR): Cc |0D -
ct(move(LV)): Mc |0D -
ct(move(CLV)): Cc |1D -
ct(fr): Cc |1D -
tr(LV): |0-
tr(LR): |0-
tr(fr): |1-
ctr(2): Pc |2D -
ctr(Fprv()): Pc |4D -
ctr(Fcprv()): Pc |5D -
ctr(LV): |0-
ctr(CLV): |1-
ctr(LR): |0-
ctr(CLR): |0-
ctr(move(LV)): |0-
ctr(move(CLV)): |1-
ctr(fr): |1-
trr(2): Pc |2D -
trr(Fprv()): Pc |4D -
trr(move(LV)): |0-
ctrr(2): Pc |2D -
ctrr(Fprv()): Pc |4D -
ctrr(Fcprv()): Pc |5D -
ctrr(move(LV)): |0-
ctrr(move(CLV)): |1-
pf(2): |Va 2-
pf(Fprv()): Pc |Ma 4D -
pf(Fcprv()): Pc |Ca 5D -
pf(LV): |Ca 0-
pf(CLV): |Ca 1-
pf(LR): |Ca 0-
pf(CLR): |Ca 0-
pf(move(LV)): |Ma 0-
pf(move(CLV)): |Ca 1-
pf(fr): |Ca 1-
From: t ct tr ctr trr ctrr pf
2 PcD PcD - PcD PcD PcD Va
Fprv() PcD PcD - PcD PcD PcD PcMaD
Fcprv() PcD PcD - PcD - PcD PcCaD
LV CcD CcD + + - - Ca
CLV CcD CcD - + - - Ca
LR CcD CcD + + - - Ca
CLR CcD CcD - + - - Ca
move(LV) McD McD - + + + Ma
move(CLV) CcD CcD - + - + Ca
fr CcD CcD + + - - Ca

D D D

*/

Links:

Overload resolution
Const reference vs move semantics
Advantages of pass by value and std::move over pass by reference

Returning from a function

When returning an expression from a function, category of returned expression FROM_TYPE can mismatch category of function return type RETURN_TYPE, which can mismatch category of assigned variable TO_TYPE: RETURN_TYPE f() { FROM_TYPE x; return x; } TO_TYPE y = f();

In this case implicit conversion can occur two times:

https://habrastorage.org/webt/70/gb/av/70gbav3z_faiwbacrdwoywvgnyw.png

Footnotes:

1 — a temporary created during assignment to hold a reference initializer persists until the end of its reference's scope (red font).

Examples and tests of variants with printing of used copy/move constructors and operators:

Examples and tests with printing of each called constructor and operator
#include <iostream>
#include <iomanip>
#include <map>
#include <vector>
#include <string>

using namespace std;

template<class C, class T>
auto contains(const C& v, const T& x)
-> decltype(end(v), true)
{
return end(v) != std::find(begin(v), end(v), x);
}

template <class... Types>
constexpr inline __attribute__((__always_inline__)) int UNUSED(Types&&...) {
return 0;
};

map<string, map<string, string>> res;
vector<string> froms;
vector<string> tos;
string from;
string to;

int ready = 0;

void report(string st) {
if (!from.empty() && !to.empty()) {
res[from][to] += st;
}
if (ready) cout << st << " ";
}

struct T {
T() {
report("Dc");
}
T(int va) : a(va) {
report("Pc");
}
T(const T& other) :
a(other.a)
{
report("Cc");
}
T(T&& other) :
a(std::exchange(other.a, 0))
{
report("Mc");
}
T& operator=(int va) {
report("Va");
a = va;
return *this;
}
T& operator=(const T& rhs) {
report("Ca");
// check for self-assignment
if(&rhs == this) return *this;
a = rhs.a;
return *this;
}
T& operator=(T&& rhs) {
report("Ma");
// check for self-assignment
if(&rhs == this) return *this;
a = std::exchange(rhs.a, 0);
return *this;
}
~T() {
report("D");
}
int a = 1;
};

T lv;
const T clv;
T& lr = lv;
T&& rr = std::move(lv);
const T& clr = clv;
const T&& crr = std::move(clv);
auto&& arr = std::move(lv);

T Fprv() { return T(); }
const T Fcprv() { return T(); }

void func_start() {
cout << "|";
}

T prv_t() { func_start(); return T(2); }
const T prv_ct() { func_start(); return T(2); }
// Prohibited (returning reference to temporary object):
// T& prv_tr() { func_start(); return T(2); }
// const T& prv_ctr() { func_start(); return T(2); }
// T&& prv_trr() { func_start(); return T(2); }
// const T&& prv_ctrr() { func_start(); return T(2); }
template<typename Z> Z&& prv_fr() { func_start(); return Z(2); }

/* Same as prv_ (I tested)
T cprv_t() { func_start(); return Fcprv(); }
const T cprv_ct() { func_start(); return Fcprv(); }
// Prohibited (returning reference to temporary object):
// T& cprv_tr() { func_start(); return Fcprv(); }
// const T& cprv_ctr() { func_start(); return Fcprv(); }
// T&& cprv_trr() { func_start(); return Fcprv(); }
// const T&& cprv_ctrr() { func_start(); return Fcprv(); }
template<typename Z> Z&& cprv_fr() { func_start(); return Fcprv(); }
*/

/* Same as prv_ (I tested)
T lit_t() { func_start(); return 3; }
const T lit_ct() { func_start(); return 3; }
//T& lit_tr() { func_start(); return 3; }
//const T& lit_ctr() { func_start(); return 3; }
//T&& lit_trr() { func_start(); return 3; }
//const T&& lit_ctrr() { func_start(); return 3; }
template<typename Z> Z&& lit_fr() { func_start(); return 3; }
*/

T lr_t() { func_start(); return lr; }
const T lr_ct() { func_start(); return lr; }
T& lr_tr() { func_start(); return lr; }
const T& lr_ctr() { func_start(); return lr; }
//T&& lr_trr() { func_start(); return lr; }
//const T&& lr_ctrr() { func_start(); return lr; }
template<typename Z> Z&& lr_fr() { func_start(); return lr; }

T clr_t() { func_start(); return clr; }
const T clr_ct() { func_start(); return clr; }
//T& clr_tr() { func_start(); return clr; }
const T& clr_ctr() { func_start(); return clr; }
//T&& clr_trr() { func_start(); return clr; }
//const T&& clr_ctrr() { func_start(); return clr; }
template<typename Z> Z&& clr_fr() { func_start(); return clr; }

/* This is the same as xv and cxv (I tested it)
T rr_t() { func_start(); return move(rr); }
const T rr_ct() { func_start(); return move(rr); }
//T& rr_tr() { func_start(); return move(rr); }
const T& rr_ctr() { func_start(); return move(rr); }
T&& rr_trr() { func_start(); return move(rr); }
const T&& rr_ctrr() { func_start(); return move(rr); }
template<typename Z> Z&& rr_fr() { func_start(); return move(rr); }

T crr_t() { func_start(); return move(crr); }
const T crr_ct() { func_start(); return move(crr); }
//T& crr_tr() { func_start(); return move(crr); }
const T& crr_ctr() { func_start(); return move(crr); }
//T&& crr_trr() { func_start(); return move(crr); }
const T&& crr_ctrr() { func_start(); return move(crr); }
template<typename Z> Z&& crr_fr() { func_start(); return move(crr); }
*/

/* Same as lr_ (I tested)
T arr_t() { func_start(); return arr; }
const T arr_ct() { func_start(); return arr; }
T& arr_tr() { func_start(); return arr; }
const T& arr_ctr() { func_start(); return arr; }
//T&& arr_trr() { func_start(); return arr; }
//const T&& arr_ctrr() { func_start(); return arr; }
template<typename Z> Z&& arr_fr() { func_start(); return arr; }
*/

T lv_t() { func_start(); return lv; }
const T lv_ct() { func_start(); return lv; }
T& lv_tr() { func_start(); return lv; }
const T& lv_ctr() { func_start(); return lv; }
//T&& lv_trr() { func_start(); return lv; }
//const T&& lv_ctrr() { func_start(); return lv; }
template<typename Z> Z&& lv_fr() { func_start(); return lv; }

T xv_t() { func_start(); return move(lv); }
const T xv_ct() { func_start(); return move(lv); }
//T& xv_tr() { func_start(); return move(lv); }
const T& xv_ctr() { func_start(); return move(lv); }
T&& xv_trr() { func_start(); return move(lv); }
const T&& xv_ctrr() { func_start(); return move(lv); }
template<typename Z> Z&& xv_fr() { func_start(); return move(lv); }

T clv_t() { func_start(); return clv; }
const T clv_ct() { func_start(); return clv; }
//T& clv_tr() { func_start(); return clv; }
const T& clv_ctr() { func_start(); return clv; }
//T&& clv_trr() { func_start(); return clv; }
//const T&& clv_ctrr() { func_start(); return clv; }
template<typename Z> Z&& clv_fr() { func_start(); return clv; }

T cxv_t() { func_start(); return move(clv); }
const T cxv_ct() { func_start(); return move(clv); }
//T& cxv_tr() { func_start(); return move(clv); }
const T& cxv_ctr() { func_start(); return move(clv); }
//T&& cxv_trr() { func_start(); return move(clv); }
const T&& cxv_ctrr() { func_start(); return move(clv); }
template<typename Z> Z&& cxv_fr() { func_start(); return move(clv); }

void print_col(const string &st, int width) {
cout << endl << left << setw(width) << st;
}

void test_call(string lto, string lfrom) {
from = lfrom;
to = lto;
res[from][to] = "";
if (!from.empty() && !to.empty()) {
if (!contains(froms, from)) froms.push_back(from);
if (!contains(tos, to)) tos.push_back(to);
}
print_col(lto + " = " + lfrom + ": ", 20);
}

#define EVAL(x) #x

#define TEST_CALL(t, v) { \
test_call(EVAL(t), #v); \
t s = v(); \
cout << s.a; \
UNUSED(s); \
cout << "-"; \
}

void test_return() {
ready = 1;
cout << endl;
#define DT T
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT const T
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT T&
//TEST_CALL(DT, prv_t);
//TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
//TEST_CALL(DT, lr_t);
//TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
//TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
//TEST_CALL(DT, clr_t);
//TEST_CALL(DT, clr_ct);
//TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
//TEST_CALL(DT, lv_t);
//TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
//TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
//TEST_CALL(DT, xv_t);
//TEST_CALL(DT, xv_ct);
//TEST_CALL(DT, xv_ctr);
//TEST_CALL(DT, xv_trr);
//TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
//TEST_CALL(DT, clv_t);
//TEST_CALL(DT, clv_ct);
//TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
//TEST_CALL(DT, cxv_t);
//TEST_CALL(DT, cxv_ct);
//TEST_CALL(DT, cxv_ctr);
//TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT T&&
TEST_CALL(DT, prv_t);
//TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
//TEST_CALL(DT, lr_ct);
//TEST_CALL(DT, lr_tr);
//TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
//TEST_CALL(DT, clr_ct);
//TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
//TEST_CALL(DT, lv_ct);
//TEST_CALL(DT, lv_tr);
//TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
//TEST_CALL(DT, xv_ct);
//TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
//TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
//TEST_CALL(DT, clv_ct);
//TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
//TEST_CALL(DT, cxv_ct);
//TEST_CALL(DT, cxv_ctr);
//TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT const T&&
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
//TEST_CALL(DT, lr_tr);
//TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
//TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
//TEST_CALL(DT, lv_tr);
//TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
//TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
//TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
//TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT const auto&&
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
//TEST_CALL(DT, lr_tr);
//TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
//TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
//TEST_CALL(DT, lv_tr);
//TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
//TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
//TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
//TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT auto&
//TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
//TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
//TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
//TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
//TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
TEST_CALL(DT, xv_ctr);
//TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
//TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
//TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT const T&
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT const auto&
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);
#undef DT
#define DT auto&&
TEST_CALL(DT, prv_t);
TEST_CALL(DT, prv_ct);
//TEST_CALL(DT, prv_fr);
TEST_CALL(DT, lr_t);
TEST_CALL(DT, lr_ct);
TEST_CALL(DT, lr_tr);
TEST_CALL(DT, lr_ctr);
//TEST_CALL(DT, lr_fr);
TEST_CALL(DT, clr_t);
TEST_CALL(DT, clr_ct);
TEST_CALL(DT, clr_ctr);
//TEST_CALL(DT, clr_fr);
TEST_CALL(DT, lv_t);
TEST_CALL(DT, lv_ct);
TEST_CALL(DT, lv_tr);
TEST_CALL(DT, lv_ctr);
//TEST_CALL(DT, lv_fr);
TEST_CALL(DT, xv_t);
TEST_CALL(DT, xv_ct);
TEST_CALL(DT, xv_ctr);
TEST_CALL(DT, xv_trr);
TEST_CALL(DT, xv_ctrr);
//TEST_CALL(DT, xv_fr);
TEST_CALL(DT, clv_t);
TEST_CALL(DT, clv_ct);
TEST_CALL(DT, clv_ctr);
//TEST_CALL(DT, clv_fr);
TEST_CALL(DT, cxv_t);
TEST_CALL(DT, cxv_ct);
TEST_CALL(DT, cxv_ctr);
TEST_CALL(DT, cxv_ctrr);
//TEST_CALL(DT, cxv_fr);

cout << endl;
const int twidth = 9;
cout << left << setw(twidth) << "From:";
for (const auto& lto : tos) {
cout << left << setw(twidth) << lto;
}
cout << endl;
for (const auto& lfrom : froms) {
cout << left << setw(twidth) << lfrom;
for (const auto& lto : tos) {
if (!res.count(lfrom) || !res[lfrom].count(lto)) {
cout << left << setw(twidth) << "-";
} else if (res[lfrom][lto].empty()) {
cout << left << setw(twidth) << "+";
} else {
cout << left << setw(twidth) << res[lfrom][lto];
}
}
cout << endl;
}
}

int main() {

test_return();

return 0;
}

/* Output:

T = prv_t: |Pc 2-D
T = prv_ct: |Pc 2-D
T = lr_t: |Cc 1-D
T = lr_ct: |Cc 1-D
T = lr_tr: |Cc 1-D
T = lr_ctr: |Cc 1-D
T = clr_t: |Cc 1-D
T = clr_ct: |Cc 1-D
T = clr_ctr: |Cc 1-D
T = lv_t: |Cc 1-D
T = lv_ct: |Cc 1-D
T = lv_tr: |Cc 1-D
T = lv_ctr: |Cc 1-D
T = xv_t: |Mc 1-D
T = xv_ct: |Mc 0-D
T = xv_ctr: |Cc 0-D
T = xv_trr: |Mc 0-D
T = xv_ctrr: |Cc 0-D
T = clv_t: |Cc 1-D
T = clv_ct: |Cc 1-D
T = clv_ctr: |Cc 1-D
T = cxv_t: |Cc 1-D
T = cxv_ct: |Cc 1-D
T = cxv_ctr: |Cc 1-D
T = cxv_ctrr: |Cc 1-D
const T = prv_t: |Pc 2-D
const T = prv_ct: |Pc 2-D
const T = lr_t: |Cc 0-D
const T = lr_ct: |Cc 0-D
const T = lr_tr: |Cc 0-D
const T = lr_ctr: |Cc 0-D
const T = clr_t: |Cc 1-D
const T = clr_ct: |Cc 1-D
const T = clr_ctr: |Cc 1-D
const T = lv_t: |Cc 0-D
const T = lv_ct: |Cc 0-D
const T = lv_tr: |Cc 0-D
const T = lv_ctr: |Cc 0-D
const T = xv_t: |Mc 0-D
const T = xv_ct: |Mc 0-D
const T = xv_ctr: |Cc 0-D
const T = xv_trr: |Mc 0-D
const T = xv_ctrr: |Cc 0-D
const T = clv_t: |Cc 1-D
const T = clv_ct: |Cc 1-D
const T = clv_ctr: |Cc 1-D
const T = cxv_t: |Cc 1-D
const T = cxv_ct: |Cc 1-D
const T = cxv_ctr: |Cc 1-D
const T = cxv_ctrr: |Cc 1-D
T& = lr_tr: |0-
T& = lv_tr: |0-
T&& = prv_t: |Pc 2-D
T&& = lr_t: |Cc 0-D
T&& = clr_t: |Cc 1-D
T&& = lv_t: |Cc 0-D
T&& = xv_t: |Mc 0-D
T&& = xv_trr: |0-
T&& = clv_t: |Cc 1-D
T&& = cxv_t: |Cc 1-D
const T&& = prv_t: |Pc 2-D
const T&& = prv_ct: |Pc 2-D
const T&& = lr_t: |Cc 0-D
const T&& = lr_ct: |Cc 0-D
const T&& = clr_t: |Cc 1-D
const T&& = clr_ct: |Cc 1-D
const T&& = lv_t: |Cc 0-D
const T&& = lv_ct: |Cc 0-D
const T&& = xv_t: |Mc 0-D
const T&& = xv_ct: |Mc 0-D
const T&& = xv_trr: |0-
const T&& = xv_ctrr: |0-
const T&& = clv_t: |Cc 1-D
const T&& = clv_ct: |Cc 1-D
const T&& = cxv_t: |Cc 1-D
const T&& = cxv_ct: |Cc 1-D
const T&& = cxv_ctrr: |1-
const auto&& = prv_t: |Pc 2-D
const auto&& = prv_ct: |Pc 2-D
const auto&& = lr_t: |Cc 0-D
const auto&& = lr_ct: |Cc 0-D
const auto&& = clr_t: |Cc 1-D
const auto&& = clr_ct: |Cc 1-D
const auto&& = lv_t: |Cc 0-D
const auto&& = lv_ct: |Cc 0-D
const auto&& = xv_t: |Mc 0-D
const auto&& = xv_ct: |Mc 0-D
const auto&& = xv_trr: |0-
const auto&& = xv_ctrr: |0-
const auto&& = clv_t: |Cc 1-D
const auto&& = clv_ct: |Cc 1-D
const auto&& = cxv_t: |Cc 1-D
const auto&& = cxv_ct: |Cc 1-D
const auto&& = cxv_ctrr: |1-
auto& = prv_ct: |Pc 2-D
auto& = lr_ct: |Cc 0-D
auto& = lr_tr: |0-
auto& = lr_ctr: |0-
auto& = clr_ct: |Cc 1-D
auto& = clr_ctr: |1-
auto& = lv_ct: |Cc 0-D
auto& = lv_tr: |0-
auto& = lv_ctr: |0-
auto& = xv_ct: |Mc 0-D
auto& = xv_ctr: |0-
auto& = xv_ctrr: |0-
auto& = clv_ct: |Cc 1-D
auto& = clv_ctr: |1-
auto& = cxv_ct: |Cc 1-D
auto& = cxv_ctr: |1-
auto& = cxv_ctrr: |1-
const T& = prv_t: |Pc 2-D
const T& = prv_ct: |Pc 2-D
const T& = lr_t: |Cc 0-D
const T& = lr_ct: |Cc 0-D
const T& = lr_tr: |0-
const T& = lr_ctr: |0-
const T& = clr_t: |Cc 1-D
const T& = clr_ct: |Cc 1-D
const T& = clr_ctr: |1-
const T& = lv_t: |Cc 0-D
const T& = lv_ct: |Cc 0-D
const T& = lv_tr: |0-
const T& = lv_ctr: |0-
const T& = xv_t: |Mc 0-D
const T& = xv_ct: |Mc 0-D
const T& = xv_ctr: |0-
const T& = xv_trr: |0-
const T& = xv_ctrr: |0-
const T& = clv_t: |Cc 1-D
const T& = clv_ct: |Cc 1-D
const T& = clv_ctr: |1-
const T& = cxv_t: |Cc 1-D
const T& = cxv_ct: |Cc 1-D
const T& = cxv_ctr: |1-
const T& = cxv_ctrr: |1-
const auto& = prv_t: |Pc 2-D
const auto& = prv_ct: |Pc 2-D
const auto& = lr_t: |Cc 0-D
const auto& = lr_ct: |Cc 0-D
const auto& = lr_tr: |0-
const auto& = lr_ctr: |0-
const auto& = clr_t: |Cc 1-D
const auto& = clr_ct: |Cc 1-D
const auto& = clr_ctr: |1-
const auto& = lv_t: |Cc 0-D
const auto& = lv_ct: |Cc 0-D
const auto& = lv_tr: |0-
const auto& = lv_ctr: |0-
const auto& = xv_t: |Mc 0-D
const auto& = xv_ct: |Mc 0-D
const auto& = xv_ctr: |0-
const auto& = xv_trr: |0-
const auto& = xv_ctrr: |0-
const auto& = clv_t: |Cc 1-D
const auto& = clv_ct: |Cc 1-D
const auto& = clv_ctr: |1-
const auto& = cxv_t: |Cc 1-D
const auto& = cxv_ct: |Cc 1-D
const auto& = cxv_ctr: |1-
const auto& = cxv_ctrr: |1-
auto&& = prv_t: |Pc 2-D
auto&& = prv_ct: |Pc 2-D
auto&& = lr_t: |Cc 0-D
auto&& = lr_ct: |Cc 0-D
auto&& = lr_tr: |0-
auto&& = lr_ctr: |0-
auto&& = clr_t: |Cc 1-D
auto&& = clr_ct: |Cc 1-D
auto&& = clr_ctr: |1-
auto&& = lv_t: |Cc 0-D
auto&& = lv_ct: |Cc 0-D
auto&& = lv_tr: |0-
auto&& = lv_ctr: |0-
auto&& = xv_t: |Mc 0-D
auto&& = xv_ct: |Mc 0-D
auto&& = xv_ctr: |0-
auto&& = xv_trr: |0-
auto&& = xv_ctrr: |0-
auto&& = clv_t: |Cc 1-D
auto&& = clv_ct: |Cc 1-D
auto&& = clv_ctr: |1-
auto&& = cxv_t: |Cc 1-D
auto&& = cxv_ct: |Cc 1-D
auto&& = cxv_ctr: |1-
auto&& = cxv_ctrr: |1-
From: T const T T& T&& const T&&const auto&&auto& const T& const auto&auto&&
prv_t PcD PcD - PcD PcD PcD - PcD PcD PcD
prv_ct PcD PcD - - PcD PcD PcD PcD PcD PcD
lr_t CcD CcD - CcD CcD CcD - CcD CcD CcD
lr_ct CcD CcD - - CcD CcD CcD CcD CcD CcD
lr_tr CcD CcD + - - - + + + +
lr_ctr CcD CcD - - - - + + + +
clr_t CcD CcD - CcD CcD CcD - CcD CcD CcD
clr_ct CcD CcD - - CcD CcD CcD CcD CcD CcD
clr_ctr CcD CcD - - - - + + + +
lv_t CcD CcD - CcD CcD CcD - CcD CcD CcD
lv_ct CcD CcD - - CcD CcD CcD CcD CcD CcD
lv_tr CcD CcD + - - - + + + +
lv_ctr CcD CcD - - - - + + + +
xv_t McD McD - McD McD McD - McD McD McD
xv_ct McD McD - - McD McD McD McD McD McD
xv_ctr CcD CcD - - - - + + + +
xv_trr McD McD - + + + - + + +
xv_ctrr CcD CcD - - + + + + + +
clv_t CcD CcD - CcD CcD CcD - CcD CcD CcD
clv_ct CcD CcD - - CcD CcD CcD CcD CcD CcD
clv_ctr CcD CcD - - - - + + + +
cxv_t CcD CcD - CcD CcD CcD - CcD CcD CcD
cxv_ct CcD CcD - - CcD CcD CcD CcD CcD CcD
cxv_ctr CcD CcD - - - - + + + +
cxv_ctrr CcD CcD - - + + + + + +

*/

std::move or std::forward should not be applied to local objects when returning from function, if return value optimization (RVO) can be used to return a local object by value — because converting LV to XV will prevent compiler from applying RVO and moving object instead. When applied, RVO is more effective, because it avoids calling any additional copy/move constructors or operators.

On the other hand, different compilers can not apply RVO in different situations, but in most situations compiler will be able to apply move assignment operator at least. If there is a risk that copy constructor will be called instead of RVO, you can use std::move or std::forward intead of returning object by value, but as far as I know the only situation when this can happen is when using ternary operator in return statement like return param ? a : b, which can be usually easily replaced with if statement. You can find more information about which compilers apply RVO in which situations https:// here and https:// here.

Test of RVO with latest clang on Ubuntu (year 2019):

Examples and tests with printing of each called constructor and operator
#include <iostream>
#include <string>
#include <iomanip>

using namespace std;

void report(string st) {
cout << st << " ";
}

struct T {
T() {
report("Dc");
}
T(int va) : a(va) {
report("Pc");
}
T(const T& other) :
a(other.a)
{
report("Cc");
}
T(T&& other) :
a(std::exchange(other.a, 0))
{
report("Mc");
}
T& operator=(int va) {
report("Va");
a = va;
return *this;
}
T& operator=(const T& rhs) {
report("Ca");
// check for self-assignment
if(&rhs == this) return *this;
a = rhs.a;
return *this;
}
T& operator=(T&& rhs) {
report("Ma");
// check for self-assignment
if(&rhs == this) return *this;
a = std::exchange(rhs.a, 0);
return *this;
}
~T() {
report("D");
}
int a = 1;
};

T urvo_single() {
//const bool param = true;
return T();
}

T urvo_two() {
const bool param = true;
if(param)
return T();
else
return T();
}

T urvo_two_with_param(bool param) {
if(param)
return T();
else
return T();
}

T urvo_with_exception_1(bool param) {
if(!param)
throw std::exception();

return T();
}

T urvo_with_exception_2(bool param) {
if(param)
return T();
else
throw std::exception();
}

T urvo_with_exception_3() {
const bool param = true;
if(param)
return T();
else
throw std::exception();
}

static T make_X() { return T(); }

T rrvo_single() {
//const bool param = true;
return make_X();
}

T rrvo_two() {
const bool param = true;
if(param)
return make_X();
else
return make_X();
}

T rrvo_two_with_param(bool param) {
if(param)
return make_X();
else
return make_X();
}

T rrvo_with_exception_1(bool param) {
if(!param)
throw std::exception();

return make_X();
}

T rrvo_with_exception_2(bool param) {
if(param)
return make_X();
else
throw std::exception();
}

T rrvo_with_exception_3() {
const bool param = true;
if(param)
return make_X();
else
throw std::exception();
}

T nrvo_single_1() {
T a;
return a;
}

T nrvo_single_2() {
{
T a;
return a;
}
}

T nrvo_single_with_exception_1(bool param) {
T a;
if(!param)
throw std::exception();

return a;
}

T nrvo_single_with_exception_1a(bool param) {
if(!param)
throw std::exception();
T a;
return a;
}

T nrvo_single_with_exception_2(bool param) {
T a;
if(param)
return a;
else
throw std::exception();

// Silence compilation error, does not count as an additional
// return statement as it is unreachable code
return a;
}

T nrvo_single_with_exception_2a(bool param) {
if(param) {
T a;
return a;
} else
throw std::exception();
}

T nrvo_single_with_exception_3() {
const bool param = true;
T a;
if(param)
return a;
else
throw std::exception();
}

T nrvo_single_with_exception_3a() {
const bool param = true;
if(param) {
T a;
return a;
} else
throw std::exception();
}

T nrvo_two_different_tern() {
const bool param = true;
T a, b;
return param ? a : b;
}

T nrvo_two_different_if() {
const bool param = true;
T a, b;
if(param)
return a;
else
return b;
}

T nrvo_two_different_if_2() {
const bool param = true;
if(param) {
T a;
return a;
} else {
T b;
return b;
}
}

T nrvo_two_different_with_param_tern(bool param) {
T a, b;
return param ? a : b;
}

T nrvo_two_different_with_param_if(bool param) {
T a, b;
if(param)
return a;
else
return b;
}

T nrvo_two_different_with_param_if_2(bool param) {
if(param) {
T a;
return a;
} else {
T b;
return b;
}
}

T nrvo_two_equal_tern() {
const bool param = true;
T a;
return param ? a : a;
}

T nrvo_two_equal_if() {
const bool param = true;
T a;
if(param)
return a;
else
return a;
}

T nrvo_two_equal_with_param_tern(bool param) {
T a;
return param ? a : a;
}

T nrvo_two_equal_with_param_if(bool param) {
T a;
if(param)
return a;
else
return a;
}

T nrvo_urvo_mixed_static() {
static const bool param = true;
if (param)
return T();
T a;
return a;
}

T nrvo_urvo_mixed_dynamic(bool param) {
if (param)
return T();
T a;
return a;
}

void print_col(const string &st, int width) {
cout << endl << left << setw(width) << st;
}

#define CHECK_COPIES(stmt) { \
print_col(#stmt ": ", 20); \
try { \
stmt; \
} \
catch(...) { \
} \
} \

int main() {
CHECK_COPIES( T a = urvo_single());
CHECK_COPIES( T a = urvo_two());
CHECK_COPIES( T a = urvo_two_with_param(true));
CHECK_COPIES( T a = urvo_with_exception_1(true));
CHECK_COPIES( T a = urvo_with_exception_2(true));
CHECK_COPIES( T a = urvo_with_exception_3());

cerr << " ";

CHECK_COPIES( T a = rrvo_single());
CHECK_COPIES( T a = rrvo_two());
CHECK_COPIES( T a = rrvo_two_with_param(true));
CHECK_COPIES( T a = rrvo_with_exception_1(true));
CHECK_COPIES( T a = rrvo_with_exception_2(true));
CHECK_COPIES( T a = rrvo_with_exception_3());

cerr << " ";

CHECK_COPIES( T a = nrvo_single_1());
CHECK_COPIES( T a = nrvo_single_2());
CHECK_COPIES( T a = nrvo_single_with_exception_1(true));
CHECK_COPIES( T a = nrvo_single_with_exception_1a(true));
CHECK_COPIES( T a = nrvo_single_with_exception_2(true));
CHECK_COPIES( T a = nrvo_single_with_exception_2a(true));
CHECK_COPIES( T a = nrvo_single_with_exception_3());
CHECK_COPIES( T a = nrvo_single_with_exception_3a());

cerr << " ";

CHECK_COPIES( T a = nrvo_two_different_tern());
CHECK_COPIES( T a = nrvo_two_different_if());
CHECK_COPIES( T a = nrvo_two_different_if_2());
CHECK_COPIES( T a = nrvo_two_different_with_param_tern(true));
CHECK_COPIES( T a = nrvo_two_different_with_param_if(true));
CHECK_COPIES( T a = nrvo_two_different_with_param_if_2(true));
CHECK_COPIES( T a = nrvo_two_equal_tern());
CHECK_COPIES( T a = nrvo_two_equal_if());
CHECK_COPIES( T a = nrvo_two_equal_with_param_tern(true));
CHECK_COPIES( T a = nrvo_two_equal_with_param_if(true));

cerr << " ";

CHECK_COPIES( T a = nrvo_urvo_mixed_static());
CHECK_COPIES( T a = nrvo_urvo_mixed_dynamic(true));
}

/* Output:

T a = urvo_single(): Dc D
T a = urvo_two(): Dc D
T a = urvo_two_with_param(true): Dc D
T a = urvo_with_exception_1(true): Dc D
T a = urvo_with_exception_2(true): Dc D
T a = urvo_with_exception_3(): Dc D
T a = rrvo_single(): Dc D
T a = rrvo_two(): Dc D
T a = rrvo_two_with_param(true): Dc D
T a = rrvo_with_exception_1(true): Dc D
T a = rrvo_with_exception_2(true): Dc D
T a = rrvo_with_exception_3(): Dc D
T a = nrvo_single_1(): Dc D
T a = nrvo_single_2(): Dc D
T a = nrvo_single_with_exception_1(true): Dc D
T a = nrvo_single_with_exception_1a(true): Dc D
T a = nrvo_single_with_exception_2(true): Dc D
T a = nrvo_single_with_exception_2a(true): Dc D
T a = nrvo_single_with_exception_3(): Dc D
T a = nrvo_single_with_exception_3a(): Dc D
T a = nrvo_two_different_tern(): Dc Dc Cc D D D
T a = nrvo_two_different_if(): Dc Dc Mc D D D
T a = nrvo_two_different_if_2(): Dc D
T a = nrvo_two_different_with_param_tern(true): Dc Dc Cc D D D
T a = nrvo_two_different_with_param_if(true): Dc Dc Mc D D D
T a = nrvo_two_different_with_param_if_2(true): Dc D
T a = nrvo_two_equal_tern(): Dc Cc D D
T a = nrvo_two_equal_if(): Dc D
T a = nrvo_two_equal_with_param_tern(true): Dc Cc D D
T a = nrvo_two_equal_with_param_if(true): Dc D
T a = nrvo_urvo_mixed_static(): Dc D
T a = nrvo_urvo_mixed_dynamic(true): Dc D

*/

Links:

Copy elision
Copy elision revisited
Compare clang and gcc copy elision (tests with similar results are commented out)

Passing and returning trivially copyable objects of small size

Trivially copyable struct or class:

Trivially copyable objects of small size (up to 16 bytes) can be passed in CPU registers when passed to function by value — like scalar types.

Also, when passing it by value, compiler has additional guarantee that object will not change during the course of the function even if other functions are called — this is why compiler can generate more effective code in this situations. If passed by const T&, there is no such guarantee, because object passed by constant reference can still be changed from other function, or multiple passed objects can use shared memory.

For the same reason, std::string_view should be passed by value.

Links:

Trivially copyable

Returning from a function with a reference parameter

A reference to an object can be passed to a function so that function can change source object:

T& change(T& X) { 
X.change();
return X;
}

This method should not be used if temporary object can be created inside function and returned by value, because returning by value can take advantage of return value optimization (see above).

Passing multiple parameters and returning one of them from function

If all passed parameters are lvalues, you can pass and return by const T&. This gives high performance, because no copy/move constructors or operators are called:

const T& get(const T& a, const T& b) {
if ( /* something */ ) return a;
else return b;
}

prvalue cannot be passed to get function, because this would result in returning a reference to a temporary object. If prvalue has to be passed,

If both parameters are temporary objects (prvalues), T&& can be returned, which also gives high performance (no copy/move constructors or operators are called):

T&& get(T&& a, T&& b) {
if ( /* something */ ) return std::move(a);
else return std::move(b);
}

If some of parameters are temporary objects (prvalues) and other are lvalues, it is difficult to high performance without complicating the function interface. In the following example if lvalue and prvalue are passed as A and B arguments into the function, copy constructor will be called if A is chosen and move constructor if B is chosen:

T get(const T& a, T&& b) {
if ( /* something */ ) return a;
else return std::move(b);
}

Passing to constructor or setter

In this section I describe passing an object of type T to an object of type C during construction of type C or by calling a setter:

class C { 
C(T X) : x(X) {}
void set(T X) {
x = X;
}
T x;
};

T y;
C c(y);
c.set(y);

Variants:

https://habrastorage.org/webt/4w/0q/ee/4w0qee7m2_tsrnmaaximni3awf4.png

In the comparison tables below constructor properties are highlighted with green, setter member function properties are highlighted with yellow.

https://habrastorage.org/webt/8w/0n/rt/8w0nrtcbpggu66xhxnc1dm9ecrk.png

Annotations:
1 — Becomes combinatorial if constructor has multiple parameters
2 — Function is technically noexcept, but copy before function call can result in exception
3 — Requires noexcept(std::is_nothrow_assignable<Type&, T>::value)
4 — Does not accept conversion
5 — Unconditional deallocation leads to inability to reuse allocated storage (std::string). Not a problem for copy-on-write implementations (Tstring)
6 — If you do not have overloaded assignment operator for value type in object class, this will be: VcMaD

Examples and tests of variants with printing of used copy/move constructors and operators:

Examples and tests with printing of each called constructor and operator
#include <iostream>

using namespace std;

struct TStruct {
TStruct() {
cout << "Dc";
}
TStruct(int va) : a(va) {
cout << "Vc";
}
TStruct(const TStruct& other) :
a(other.a)
{
cout << "Cc";
}
TStruct(TStruct&& other) :
a(std::exchange(other.a, 0))
{
cout << "Mc";
}
TStruct& operator=(int va) {
cout << "Va";
a = va;
return *this;
}
TStruct& operator=(const TStruct& rhs) {
cout << "Ca";
// check for self-assignment
if(&rhs == this) return *this;
a = rhs.a;
return *this;
}
TStruct& operator=(TStruct&& rhs) {
cout << "Ma";
// check for self-assignment
if(&rhs == this) return *this;
a = std::exchange(rhs.a, 0);
return *this;
}
~TStruct() {
cout << "D";
}
int a = 1;
};

struct TRef {
TRef(TStruct& tsv) : ts(tsv) {}
void set(TStruct& tsv) { ts = tsv; }
TStruct ts;
};

struct TConstRef {
TConstRef(const TStruct& tsv) : ts(tsv) {}
void set(const TStruct& tsv) { ts = tsv; }
TStruct ts;
};

struct TConstRefAndRvalueRef {
TConstRefAndRvalueRef(const TStruct& tsv) : ts(tsv) {
cout << "Lr";
}
TConstRefAndRvalueRef(TStruct&& tsv) : ts(std::move(tsv)) {
cout << "Rr";
}
void set(const TStruct& tsv) {
cout << "Sl";
ts = tsv;
}
void set(TStruct&& tsv) {
cout << "Sr";
ts = std::move(tsv);
}
TStruct ts;
};

struct TConstRvalueRef {
TConstRvalueRef(const TStruct&& tsv) : ts(std::move(tsv)) {
}
void set(const TStruct&& tsv) {
ts = std::move(tsv);
}
TStruct ts;
};

struct TValueMove {
TValueMove(TStruct tsv) : ts(std::move(tsv)) {}
void set(TStruct tsv) { ts = std::move(tsv); }
TStruct ts;
};

struct TConstValueMove {
TConstValueMove(const TStruct tsv) : ts(std::move(tsv)) {}
void set(const TStruct tsv) { ts = std::move(tsv); }
TStruct ts;
};

struct TPerfectForward {
template<class T>
TPerfectForward(T&& tsv) : ts(std::forward<T>(tsv)) {}
template<class T>
void set(T&& tsv) { ts = std::forward<T>(tsv); }
TStruct ts;
};

struct TConstPerfectForward {
template<class T>
TConstPerfectForward(const T&& tsv) : ts(std::forward<T>(tsv)) {}
template<class T>
void set(const T&& tsv) { ts = std::forward<T>(tsv); }
TStruct ts;
};

struct TPerfectForwardStrict {
template<class T, class=std::enable_if_t<std::is_same<std::decay_t<T>, TStruct>::value>>
TPerfectForwardStrict(T&& tsv) : ts(std::forward<T>(tsv)) {}
template<class T, class=std::enable_if_t<std::is_same<std::decay_t<T>, TStruct>::value>>
void set(T&& tsv) { ts = std::forward<T>(tsv); }
TStruct ts;
};

struct TMultiVariant {
TMultiVariant(const TStruct& tsv) : ts(tsv) {
cout << "Lr";
}
TMultiVariant(TStruct&& tsv) : ts(std::move(tsv)) {
cout << "Rr";
}
void set(const TStruct& tsv) {
cout << "Sl";
ts = tsv;
}
void set(TStruct&& tsv) {
cout << "Sr";
ts = std::move(tsv);
}

template<class T>
TMultiVariant(T&& tsv) : ts(std::forward<T>(tsv)) {
cout << "Pf";
}
template<class T>
void set(T&& tsv) {
cout << "Sp";
ts = std::forward<T>(tsv);
}
TStruct ts;
};

struct TMultiVariantStrict {
TMultiVariantStrict(const TStruct& tsv) : ts(tsv) {
cout << "Lr";
}
TMultiVariantStrict(TStruct&& tsv) : ts(std::move(tsv)) {
cout << "Rr";
}
void set(const TStruct& tsv) {
cout << "Sl";
ts = tsv;
}
void set(TStruct&& tsv) {
cout << "Sr";
ts = std::move(tsv);
}

template<class T, class=std::enable_if_t<std::is_same<std::decay_t<T>, TStruct>::value>>
TMultiVariantStrict(T&& tsv) : ts(std::forward<T>(tsv)) {
cout << "Pf";
}
template<class T, class=std::enable_if_t<std::is_same<std::decay_t<T>, TStruct>::value>>
void set(T&& tsv) {
cout << "Sp";
ts = std::forward<T>(tsv);
}
TStruct ts;
};

TStruct get_st() {
return TStruct(7);
}

void detect_change(int& param, int val) {
if (param != val) {
cout << "!";
param = val;
} else {
cout << "-";
}
}

void test_passing() {
cout << "TStruct st(100): ";
TStruct st(100);

// Test constructing with object
cout << endl << "TRef (st): ";
TRef rf(st);
detect_change(st.a, 100);
cout << endl << "TConstRef (st): ";
TConstRef crf(st);
detect_change(st.a, 100);
cout << endl << "TConstRefAndRvalueRef (st): ";
TConstRefAndRvalueRef crf_rvf(st);
detect_change(st.a, 100);
// Will not compile
// cout << endl << "TConstRvalueRef (st): ";
// TConstRvalueRef crvf(st);
// detect_change(st.a, 100);
cout << endl << "TValueMove (st): ";
TValueMove vm(st);
detect_change(st.a, 100);
cout << endl << "TConstValueMove (st): ";
TConstValueMove cvm(st);
detect_change(st.a, 100);
cout << endl << "TPerfectForward (st): ";
TPerfectForward pf(st);
detect_change(st.a, 100);
// Will not compile
// cout << endl << "TConstPerfectForward (st): ";
// TConstPerfectForward cpf(st);
// detect_change(st.a, 100);
cout << endl << "TPerfectForwardStrict (st): ";
TPerfectForwardStrict pft(st);
detect_change(st.a, 100);
cout << endl << "TMultiVariant (st): ";
TMultiVariant mv(st);
detect_change(st.a, 100);
cout << endl << "TMultiVariantStrict (st): ";
TMultiVariantStrict mvt(st);
detect_change(st.a, 100);

// Test constructing with moved object
// Will not compile because of std::move
// cout << endl << "TRef (std::move(st)): ";
// TRef rf4(std::move(st));
// detect_change(st.a, 100);
cout << endl << "TConstRef (std::move(st)): ";
TConstRef crf4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TConstRefAndRvalueRef (std::move(st)): ";
TConstRefAndRvalueRef crf_rvf4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TConstRvalueRef (std::move(st)): ";
TConstRvalueRef crvf4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TValueMove (std::move(st)): ";
TValueMove vm4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TConstValueMove (std::move(st)): ";
TConstValueMove cvm4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TPerfectForward (std::move(st)): ";
TPerfectForward pf4(std::move(st));
detect_change(st.a, 100);
// Will not compile
// cout << endl << "TConstPerfectForward (std::move(st)): ";
// TConstPerfectForward cpf4(std::move(st));
// detect_change(st.a, 100);
cout << endl << "TPerfectForwardStrict (std::move(st)): ";
TPerfectForwardStrict pft4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TMultiVariant (std::move(st)): ";
TMultiVariant mv4(std::move(st));
detect_change(st.a, 100);
cout << endl << "TMultiVariantStrict (std::move(st)): ";
TMultiVariantStrict mvt4(std::move(st));
detect_change(st.a, 100);

// Test constructing with temporary object
// Will not compile
// cout << endl << "TRef (TStruct(6)): ";
// TRef rf3(TStruct(6));
cout << endl << "TConstRef (TStruct(6)): ";
TConstRef crf3(TStruct(6));
cout << endl << "TConstRefAndRvalueRef (TStruct(6)): ";
TConstRefAndRvalueRef crf_rvf3(TStruct(6));
cout << endl << "TConstRvalueRef (TStruct(6)): ";
TConstRvalueRef crvf3(TStruct(6));
cout << endl << "TValueMove (TStruct(6)): ";
TValueMove vm3(TStruct(6));
cout << endl << "TConstValueMove (TStruct(6)): ";
TConstValueMove cvm3(TStruct(6));
cout << endl << "TPerfectForward (TStruct(6)): ";
TPerfectForward pf3(TStruct(6));
// Will not compile
//cout << endl << "TConstPerfectForward (TStruct(6)): ";
//TConstPerfectForward cpf3(TStruct(6));
cout << endl << "TPerfectForwardStrict (TStruct(6)): ";
TPerfectForwardStrict pft3(TStruct(6));
cout << endl << "TMultiVariant (TStruct(6)): ";
TMultiVariant mv3(TStruct(6));
cout << endl << "TMultiVariantStrict (TStruct(6)): ";
TMultiVariantStrict mvt3(TStruct(6));

// Test constructing with RVO object
// Will not compile
//cout << endl << "TRef (get_st()): ";
//TRef rf5(get_st());
cout << endl << "TConstRef (get_st()): ";
TConstRef crf5(get_st());
cout << endl << "TConstRefAndRvalueRef (get_st()): ";
TConstRefAndRvalueRef crf_rvf5(get_st());
cout << endl << "TConstRvalueRef (get_st()): ";
TConstRvalueRef crvf5(get_st());
cout << endl << "TValueMove (get_st()): ";
TValueMove vm5(get_st());
cout << endl << "TConstValueMove (get_st()): ";
TConstValueMove cvm5(get_st());
cout << endl << "TPerfectForward (get_st()): ";
TPerfectForward pf5(get_st());
//cout << endl << "TConstPerfectForward (get_st()): ";
//TConstPerfectForward cpf5(get_st());
cout << endl << "TPerfectForwardStrict (get_st()): ";
TPerfectForwardStrict pft5(get_st());
cout << endl << "TMultiVariant (get_st()): ";
TMultiVariant mv5(get_st());
cout << endl << "TMultiVariantStrict (get_st()): ";
TMultiVariantStrict mvt5(get_st());

// Test constructing with literal
// Will not compile
//cout << endl << "TRef (5): ";
//TRef rf2(5);
cout << endl << "TConstRef (5): ";
TConstRef crf2(5);
cout << endl << "TConstRefAndRvalueRef (5): ";
TConstRefAndRvalueRef crf_rvf2(5);
cout << endl << "TConstRvalueRef (5): ";
TConstRvalueRef crvf2(5);
cout << endl << "TValueMove (5): ";
TValueMove vm2(5);
cout << endl << "TConstValueMove (5): ";
TConstValueMove cvm2(5);
cout << endl << "TPerfectForward (5): ";
TPerfectForward pf2(5);
// Will not compile
//cout << endl << "TConstPerfectForward (5): ";
//TConstPerfectForward cpf2(5);
// Will not compile due to SFINAE test
//cout << endl << "TPerfectForwardStrict (5): ";
//TPerfectForwardStrict pft2(5);
cout << endl << "TMultiVariant (5): ";
TMultiVariant mv2(5);
cout << endl << "TMultiVariantStrict (5): ";
TMultiVariantStrict mvt2(5);

// Test setting with object
st.a = 100;
cout << endl << "TRef set(st): ";
rf.set(st);
detect_change(st.a, 100);
cout << endl << "TConstRef set(st): ";
crf.set(st);
detect_change(st.a, 100);
cout << endl << "TConstRefAndRvalueRef set(st): ";
crf_rvf.set(st);
detect_change(st.a, 100);
// Will not compile
// cout << endl << "TConstRvalueRef set(st): ";
// crvf2.set(st);
// detect_change(st.a, 100);
cout << endl << "TValueMove set(st): ";
vm.set(st);
detect_change(st.a, 100);
cout << endl << "TConstValueMove set(st): ";
cvm.set(st);
detect_change(st.a, 100);
cout << endl << "TPerfectForward set(st): ";
pf.set(st);
detect_change(st.a, 100);
cout << endl << "TPerfectForwardStrict set(st): ";
pft.set(st);
detect_change(st.a, 100);
cout << endl << "TMultiVariant set(st): ";
mv.set(st);
detect_change(st.a, 100);
cout << endl << "TMultiVariantStrict set(st): ";
mvt.set(st);
detect_change(st.a, 100);

// Test setting with moved object
// Will not compile
//cout << endl << "TRef set(std::move(st)): ";
//rf.set(std::move(st));
//detect_change(st.a, 100);
cout << endl << "TConstRef set(std::move(st)): ";
crf.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TConstRefAndRvalueRef set(std::move(st)): ";
crf_rvf.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TConstRvalueRef set(std::move(st)): ";
crvf2.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TValueMove set(std::move(st)): ";
vm.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TConstValueMove set(std::move(st)): ";
cvm.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TPerfectForward set(std::move(st)): ";
pf.set(std::move(st));
detect_change(st.a, 100);
detect_change(st.a, 100);
cout << endl << "TPerfectForwardStrict set(std::move(st)): ";
pft.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TMultiVariant set(std::move(st)): ";
mv.set(std::move(st));
detect_change(st.a, 100);
cout << endl << "TMultiVariantStrict set(std::move(st)): ";
mvt.set(std::move(st));
detect_change(st.a, 100);

// Test setting with constructed temporary object
// Will not compile
//cout << endl << "TRef set(TStruct(8)): ";
//rf.set(TStruct(8));
cout << endl << "TConstRef set(TStruct(8)): ";
crf.set(TStruct(8));
cout << endl << "TConstRefAndRvalueRef set(TStruct(8)): ";
crf_rvf.set(TStruct(8));
cout << endl << "TConstRvalueRef set(TStruct(8)): ";
crvf2.set(TStruct(8));
cout << endl << "TValueMove set(TStruct(8)): ";
vm.set(TStruct(8));
cout << endl << "TConstValueMove set(TStruct(8)): ";
cvm.set(TStruct(8));
cout << endl << "TPerfectForward set(TStruct(8)): ";
pf.set(TStruct(8));
cout << endl << "TPerfectForwardStrict set(TStruct(8)): ";
pft.set(TStruct(8));
cout << endl << "TMultiVariant set(TStruct(8)): ";
mv.set(TStruct(8));
cout << endl << "TMultiVariantStrict set(TStruct(8)): ";
mvt.set(TStruct(8));

// Test setting with RVO object
// Will not compile
//cout << endl << "TRef set(get_st()): ";
//rf.set(get_st());
cout << endl << "TConstRef set(get_st()): ";
crf.set(get_st());
cout << endl << "TConstRefAndRvalueRef set(get_st()): ";
crf_rvf.set(get_st());
cout << endl << "TConstRvalueRef set(get_st()): ";
crvf2.set(get_st());
cout << endl << "TValueMove set(get_st()): ";
vm.set(get_st());
cout << endl << "TConstValueMove set(get_st()): ";
cvm.set(get_st());
cout << endl << "TPerfectForward set(get_st()): ";
pf.set(get_st());
cout << endl << "TPerfectForwardStrict set(get_st()): ";
pft.set(get_st());
cout << endl << "TMultiVariant set(get_st()): ";
mv.set(get_st());
cout << endl << "TMultiVariantStrict set(get_st()): ";
mvt.set(get_st());

cout << endl << "st = 3: ";
st = 3;

// Test setting with literal
// Will not compile
//cout << endl << "TRef set(4): ";
//rf.set(4);
cout << endl << "TConstRef set(4): ";
crf.set(4);
cout << endl << "TConstRefAndRvalueRef set(4): ";
crf_rvf.set(4);
cout << endl << "TConstRvalueRef set(4): ";
crvf2.set(4);
cout << endl << "TValueMove set(4): ";
vm.set(4);
cout << endl << "TConstValueMove set(4): ";
cvm.set(4);
cout << endl << "TPerfectForward set(4): ";
pf.set(4);
// Will not compile due to SFINAE test
//cout << endl << "TPerfectForwardStrict set(st): ";
//pft.set(4);
cout << endl << "TMultiVariant set(4): ";
mv.set(4);
cout << endl << "TMultiVariantStrict set(4): ";
mvt.set(4);
cout << endl;
}

int main() {
test_passing();

return 0;
}

/* Output:

TStruct st(100): Vc
TRef (st): Cc-
TConstRef (st): Cc-
TConstRefAndRvalueRef (st): CcLr-
TValueMove (st): CcMcD-
TConstValueMove (st): CcCcD-
TPerfectForward (st): Cc-
TPerfectForwardStrict (st): Cc-
TMultiVariant (st): CcPf-
TMultiVariantStrict (st): CcPf-
TConstRef (std::move(st)): Cc-
TConstRefAndRvalueRef (std::move(st)): McRr!
TConstRvalueRef (std::move(st)): Cc-
TValueMove (std::move(st)): McMcD!
TConstValueMove (std::move(st)): McCcD!
TPerfectForward (std::move(st)): Mc!
TPerfectForwardStrict (std::move(st)): Mc!
TMultiVariant (std::move(st)): McRr!
TMultiVariantStrict (std::move(st)): McRr!
TConstRef (TStruct(6)): VcCcD
TConstRefAndRvalueRef (TStruct(6)): VcMcRrD
TConstRvalueRef (TStruct(6)): VcCcD
TValueMove (TStruct(6)): VcMcD
TConstValueMove (TStruct(6)): VcCcD
TPerfectForward (TStruct(6)): VcMcD
TPerfectForwardStrict (TStruct(6)): VcMcD
TMultiVariant (TStruct(6)): VcMcRrD
TMultiVariantStrict (TStruct(6)): VcMcRrD
TConstRef (get_st()): VcCcD
TConstRefAndRvalueRef (get_st()): VcMcRrD
TConstRvalueRef (get_st()): VcCcD
TValueMove (get_st()): VcMcD
TConstValueMove (get_st()): VcCcD
TPerfectForward (get_st()): VcMcD
TPerfectForwardStrict (get_st()): VcMcD
TMultiVariant (get_st()): VcMcRrD
TMultiVariantStrict (get_st()): VcMcRrD
TConstRef (5): VcCcD
TConstRefAndRvalueRef (5): VcMcRrD
TConstRvalueRef (5): VcCcD
TValueMove (5): VcMcD
TConstValueMove (5): VcCcD
TPerfectForward (5): Vc
TMultiVariant (5): VcPf
TMultiVariantStrict (5): VcMcRrD
TRef set(st): Ca-
TConstRef set(st): Ca-
TConstRefAndRvalueRef set(st): SlCa-
TValueMove set(st): CcMaD-
TConstValueMove set(st): CcCaD-
TPerfectForward set(st): Ca-
TPerfectForwardStrict set(st): Ca-
TMultiVariant set(st): SpCa-
TMultiVariantStrict set(st): SpCa-
TConstRef set(std::move(st)): Ca-
TConstRefAndRvalueRef set(std::move(st)): SrMa!
TConstRvalueRef set(std::move(st)): Ca-
TValueMove set(std::move(st)): McMaD!
TConstValueMove set(std::move(st)): McCaD!
TPerfectForward set(std::move(st)): Ma!-
TPerfectForwardStrict set(std::move(st)): Ma!
TMultiVariant set(std::move(st)): SrMa!
TMultiVariantStrict set(std::move(st)): SrMa!
TConstRef set(TStruct(8)): VcCaD
TConstRefAndRvalueRef set(TStruct(8)): VcSrMaD
TConstRvalueRef set(TStruct(8)): VcCaD
TValueMove set(TStruct(8)): VcMaD
TConstValueMove set(TStruct(8)): VcCaD
TPerfectForward set(TStruct(8)): VcMaD
TPerfectForwardStrict set(TStruct(8)): VcMaD
TMultiVariant set(TStruct(8)): VcSrMaD
TMultiVariantStrict set(TStruct(8)): VcSrMaD
TConstRef set(get_st()): VcCaD
TConstRefAndRvalueRef set(get_st()): VcSrMaD
TConstRvalueRef set(get_st()): VcCaD
TValueMove set(get_st()): VcMaD
TConstValueMove set(get_st()): VcCaD
TPerfectForward set(get_st()): VcMaD
TPerfectForwardStrict set(get_st()): VcMaD
TMultiVariant set(get_st()): VcSrMaD
TMultiVariantStrict set(get_st()): VcSrMaD
st = 3: Va
TConstRef set(4): VcCaD
TConstRefAndRvalueRef set(4): VcSrMaD
TConstRvalueRef set(4): VcCaD
TValueMove set(4): VcMaD
TConstValueMove set(4): VcCaD
TPerfectForward set(4): Va
TMultiVariant set(4): SpVa
TMultiVariantStrict set(4): VcSrMaD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

*/

Links:
CppCon 2014: Herb Sutter "Back to the Basics! Essentials of Modern C++ Style"
PDF presentation
Meaning of acronym SSO in the context of std::string
Why is value taking setter member functions not recommended in Herb Sutter's CppCon 2014 talk (Back to Basics: Modern C++ Style)?
What's the correct enable_if constraint on perfect forwarding setter?
Beware of Perfect Forwarding Constructors

Why other variants of passing to constructor or setter are less used

https://habrastorage.org/webt/yh/yw/lu/yhywlupwrd8x9mkmessxd8tohoc.png

Working with smart pointers

https://habrastorage.org/webt/oz/kw/dd/ozkwddosyw2igdar_pdmycwws0m.png

Links:

CppCon 2014: Herb Sutter "Back to the Basics! Essentials of Modern C++ Style"
PDF presentation
What is an 'aliased local shared_ptr' in this example?

Some of the used images were taken from the linked articles


Go to Part 1