c&c++语言参考手册
← 转到第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.
当将参数传递给函数时,传递的表达式的类别将隐式转换为函数参数的类别: void f(TO_TYPE p); FROM_TYPE x; f(x);
void f(TO_TYPE p); FROM_TYPE x; f(x);
除了函数定义不能包含“自动”类型外,这种隐式转换的方式与分配期间的方式相同(请参见上面的“分配”部分)。
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):
当函数具有相同数量和类型的参数但参数类别不同的多个重载时,重载机制将根据类别选择最适用的函数重载。 在下表中,为函数重载分配了一个顺序,在传递特定类别的表达式时,将选择每个重载(例如,1表示始终会首先选择该重载(如果可用)):
Footnotes:
脚注:
1 — a temporary created to hold a reference initializer persists until the end of function scope.
1-创建的用于保存引用初始化程序的临时目录会一直保留到函数作用域结束。
Note:
注意:
- If a function takes parameter by value, this does not mean that it always creates copy of object and cannot change source object. For example, if value is moved this function, move constructor will be called and source object will be changed. 如果函数按值接受参数,这并不意味着它总是创建对象的副本并且不能更改源对象。 例如,如果移动此函数的值,则将调用move构造函数,并更改源对象。
You cannot have both
T f()
andconst T f()
function overloads您不能同时具有
T f()
和const T f()
函数重载- Function can be overloaded based on whether a parameter is a reference or not. But if both functions can take argument, ambiguity will need to be resolved manual (by casting to the relevant function pointer type). 可以根据参数是否为引用来重载函数。 但是,如果两个函数都可以接受参数,则需要手动解决歧义(通过强制转换为相关的函数指针类型)。
- Forwarding reference is most "greedy" — it wins as soon as there is no overload with exact match. That's why overloading a forwarding reference is usually not recommended (instead, avoid FR or avoid overloading or use tag dispatch or SFINAE). Constructor with one forwarding reference parameter should not be used, because it can conflict with copy and move constructors. 转发参考是最“贪婪的”-只要没有完全匹配的重载,它就会赢。 这就是通常不建议重载转发引用的原因(相反,避免FR或避免重载或使用标签分发或SFINAE)。 不应使用带有一个转发引用参数的构造函数,因为它可能与复制和移动构造函数冲突。
- Forwarding references can make compiler errors more difficult to understand and fix, especially if they are used multiple times sequentially. 转发引用会使编译器错误更加难以理解和解决,特别是如果顺序使用它们多次。
Forwarding reference does not work in the following situations:
转发参考在以下情况下不起作用:
You cannot forward
{1, 2, 3}
to avector<int>
with a forwarding reference.您不能将
{1, 2, 3}
转发到带有转发参考的vector<int>
上。You cannot forward
0
orNULL
to a pointer with a forwarding reference (usenullptr
instead).您不能将
0
或NULL
转发给具有转发引用的指针(请改用nullptr
)。- You cannot forward integer static const and constexpr member variables with a forwarding reference, if they have no definitions. 如果没有定义,则不能使用转发参考转发整数静态const和constexpr成员变量。
- You cannot forward overloaded function or template with a forwarding reference, because this creates ambiguity (can be disambiguated by specifying type). 您不能使用转发参考转发重载的函数或模板,因为这会造成歧义(可以通过指定类型来消除歧义)。
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
重载解析 Const引用vs移动语义 按值传递和std :: move的优点按引用传递
从函数返回 (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();
从函数返回表达式时,返回表达式FROM_TYPE的类别可能与函数返回类型RETURN_TYPE的类别不匹配,这可能与分配变量TO_TYPE的类别不匹配: RETURN_TYPE f() { FROM_TYPE x; return x; } TO_TYPE y = f();
RETURN_TYPE f() { FROM_TYPE x; return x; } TO_TYPE y = f();
In this case implicit conversion can occur two times:
在这种情况下,隐式转换可能发生两次:
When converting from FROM_TYPE to RETURN TYPE. This conversion is the same as during the assignment, except trying to convert PRV or CPRV to T&&, const T&& or const T& (such conversions cannot occur, because lifetime of temporary object cannot be extended longer than function scope, link)
从FROM_TYPE转换为RETURN TYPE时。 此转换与分配期间的转换相同,只是尝试将PRV或CPRV转换为T &&,const T &&或const T&(不会发生这种转换,因为临时对象的生存期不能超过函数作用域的范围, link )
- When converting from RETURN_TYPE to TO_TYPE — a usual assignment conversion (see "Assigning value categories" above). 从RETURN_TYPE转换为TO_TYPE时-通常的分配转换(请参见上面的“分配值类别”)。
Footnotes:
脚注:
1 — a temporary created during assignment to hold a reference initializer persists until the end of its reference's scope (red font).
1-在赋值期间创建的用于保存引用初始化程序的临时对象将一直保留到其引用范围(红色字体)的结尾。
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.
从函数返回时,如果可以使用返回值优化(RVO)按值返回本地对象,则不应将std :: move或std :: forward应用于本地对象-因为将LV转换为XV将阻止编译器应用RVO和移动对象。 当应用RVO时,它会更有效,因为它避免调用任何其他复制/移动构造函数或运算符。
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.
另一方面,不同的编译器不能在不同情况下应用RVO,但是在大多数情况下,编译器将至少能够应用移动分配运算符。 如果存在将调用复制构造函数而不是RVO的风险,则可以使用std :: move或std :: forward intead按值返回对象,但是据我所知,唯一可能发生的情况是使用返回语句中的三元运算符,例如return param ? a : b
return param ? a : b
,通常可以很容易地用if语句替换。 您可以在https://这里和https://这里找到有关在哪些情况下哪些编译器应用RVO的更多信息。
Test of RVO with latest clang on Ubuntu (year 2019):
在Ubuntu上使用最新的Clang测试RVO(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)
复制省略 复制省略重新 比较铛和gcc复制省略(具有类似的结果测试被注释掉)
传递和返回小尺寸的可复制对象 (Passing and returning trivially copyable objects of small size)
Trivially copyable struct or class:
普通可复制的结构或类:
- has no virtual members or virtual base classes 没有虚拟成员或虚拟基类
- has default copy and move constructors and operators 具有默认的复制和移动构造函数和运算符
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.
按值传递给函数(例如标量类型)时,可以将较小的琐碎可复制对象(最多16个字节)传递给CPU寄存器。
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.
同样,当按值传递它时,编译器还具有保证,即使调用了其他函数,对象也不会在函数执行过程中发生变化-这就是为什么编译器在这种情况下可以生成更有效的代码的原因。 如果由const T&传递,则没有这种保证,因为通过常量引用传递的对象仍然可以从其他函数中更改,或者多个传递的对象可以使用共享内存。
For the same reason, std::string_view should be passed by value.
出于相同的原因,应该按值传递std :: string_view。
Links:
链接:
从具有参考参数的函数返回 (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&传递并返回。 这提供了高性能,因为没有调用复制/移动构造函数或运算符:
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,
无法将prvalue传递给get函数,因为这将导致返回对临时对象的引用。 如果必须传递prvalue,
If both parameters are temporary objects (prvalues), T&& can be returned, which also gives high performance (no copy/move constructors or operators are called):
如果两个参数都是临时对象(prvalue),则可以返回T &&,这也可以提供高性能(不调用复制/移动构造函数或运算符):
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:
如果某些参数是临时对象(prvalue)而其他参数是lvalues,则在不使功能接口复杂化的情况下很难实现高性能。 在下面的示例中,如果将左值和右值作为A和B参数传递给函数,则选择A时将调用复制构造函数,如果选择B则将移动构造函数:
T get(const T& a, T&& b) {
if ( /* something */ ) return a;
else return std::move(b);
}
传递给构造函数或setter (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:
在本节中,我描述在构造C类型时或通过调用setter将T类型的对象传递给C类型的对象:
class C {
C(T X) : x(X) {}
void set(T X) {
x = X;
}
T x;
};
T y;
C c(y);
c.set(y);
For copying an lvalue to x member variable in an object of class C:
要将左值复制到类C的对象中的x成员变量中:
- with class C constructor: any of the following variants is good ("T move" variant is a bit less efficient due to calling an additional move constructor) 使用C类构造函数:以下任何一种变体都不错(“ T move”变体由于调用了额外的move构造函数而效率较低)
- with class C set(T): 使用C类set(T):
- if T is copy-on-write (like CString in MFC/ATL) and x will not change later, any of the following variants is good ("T move" variant is a bit less efficient due to calling an additional move constructor) 如果T是写时复制的(例如MFC / ATL中的CString)并且x以后不会更改,则以下任何变体都不错(由于调用了额外的move构造函数,“ T move”变体的效率较低)
- if T is not copy-on write or x can change later, "T move" variant becomes inefficient, because it does not allow to avoid deallocation, even if already allocated memory in existing object x is enough for new object and class can reuse allocated memory (like std::string) 如果T不是复制写操作,或者x以后可以更改,则“ T move”变体会变得无效,因为它不允许避免取消分配,即使现有对象x中已分配的内存足以用于新对象,并且类也可以重用已分配的内存内存(如std :: string)
For moving an xvalue or prvalue to x member variable in an object of class C with C's constructor or C's
set
member function any of the following variants is good except "const T&" (because it requires copying). Perfect forwarding has a slight performance advantage due to avoiding calling move constructor when converting from different type, instead forwarding parameter of a different type to a parametrized constructor对于使用C的构造函数或C的
set
成员函数set
C类的对象中的xvalue或prvalue移到x成员变量中,除“ const T&”(因为它需要复制)外,以下任何变体都是好的。 完美转发具有少许性能优势,因为避免了从不同类型转换时调用move构造函数,而是将不同类型的参数转发给参数化的构造函数
Variants:
变体:
const T& — optimal if we do not need to move objects or store them in dynamically growing containers
const T&—如果我们不需要移动对象或将它们存储在动态增长的容器中,则为最佳选择
class C { C(const T& X) : x(X) {} void set(const T& X) { x = X; } T x; };
const T& + T&& move — optimized variant if we need to support both lvalue copy and rvalue move. Can lead to code duplication, especially if constructors with different combinations of & and && are needed. Provides simple implementation of noexcept for effective use with containers.
const T&+ T && move —如果我们需要同时支持左值复制和右值移动,则为优化变体。 可能导致代码重复,特别是在需要使用&和&&的不同组合的构造函数时。 提供noexcept的简单实现以有效地与容器一起使用。
class C { C(const T& X) : x(X) {} C(T&& X) : x(std::move(X)) {} void set(const T& X) { x = X; } void set(T&& X) { x = std::move(X); } T x; };
T move — looks simple, but not very intuitive (can avoid copying and change object, passed with std::move as xvalue). Less efficient than perfect forwarding and &+&& in some situations (does not allow to avoid deallocation, even if already allocated memory in existing object x is enough for new object and class can reuse allocated memory, calls excessive moves and destructors)
T move-看起来很简单,但不是很直观(可以避免复制和更改对象,并以std :: move作为xvalue传递)。 在某些情况下,效率低于完美转发和&+ &&的效率(不允许避免取消分配,即使现有对象x中已经分配的内存足以用于新对象,并且类可以重用分配的内存,调用过多的移动和析构函数)
class C { C(T X) : x(std::move(X)) {} void set(T X) { x = std::move(X); } T x; };
Perfect forwarding (PF) — most effective variant (especially when forwarding literals or other types, which can be converted to class with parametrized constructors), but it is more difficult to write, which increases risk of mistakes. Does not support virtual functions and has to be implemented in header file.
完美转发(PF)-最有效的变体(尤其是在转发文字或其他类型时,可以使用参数化构造函数将其转换为类),但是编写起来更加困难,这增加了出错的风险。 不支持虚拟功能,必须在头文件中实现。
class C { template<typename Z> C(Z&& X) : x(std::forward<Z>(X)) {} template<typename Z> void set(Z&& X) { x = std::forward<Z>(X); } T x; };
Strict perfect forwarding (PF Strict). Has same qualities as perfect forwarding, but is more safe, because it does not allow to convert types and thus cannot receive literals (actually, it is the only variant among discussed in this section, which does not allow to convert types).
严格的完美转发(PF Strict)。 具有与完美转发相同的质量,但更安全,因为它不允许转换类型,因此不能接收文字(实际上,这是本节中讨论的唯一变体,不允许转换类型)。
class C { template<class Z, class=std::enable_if_t<std::is_same<std::decay_t<Z>, T>::value>> C(Z&& X) : x(std::forward<Z>(X)) {} template<class Z, class=std::enable_if_t<std::is_same<std::decay_t<Z>, T>::value>> void set(Z&& X) { x = std::forward<Z>(X); } T x; };
In the comparison tables below constructor properties are highlighted with green, setter member function properties are highlighted with yellow.
在下面的比较表中,构造函数属性以绿色突出显示,setter成员函数属性以黄色突出显示。
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
注释: 1-如果构造函数具有多个参数,则变为组合2-从技术上讲,函数是noexcept,但在函数调用之前进行复制会导致异常3-要求noexcept(std :: is_nothrow_assignable <Type&,T> :: value)4-不接受转换5 —无条件释放会导致无法重用已分配的存储(std :: string)。 对于写时复制实现(Tstring)6来说不是问题-如果您没有对象类中值类型的重载赋值运算符,则为: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
链接: CppCon 2014:Herb Sutter“回到基础!现代C ++风格的要点” PDF演示文稿 在std :: string上下文中的首字母缩写词SSO的含义 为什么不建议在Herb Sutter的CppCon 2014演讲中建议采用值设定方法成员函数(返回基础知识:现代C ++风格)? 完美转发设置器What's the correct enable_if
约束是什么? 提防完善的转发构造函数
为什么较少使用传递给构造函数或setter的其他变体 (Why other variants of passing to constructor or setter are less used)
T& — cannot accept rvalue. Is the same as "const T&" variant, but less safe.
T&—不能接受右值。 与“ const T&”变体相同,但安全性较低。
class T { C(T& X) : x(X) {} void set(T& X) { x = X; } T x; };
const T&& move — does not allow move due to prohibiting change of passed object. Not used.
const T && move —由于禁止更改传递的对象,因此不允许移动。 不曾用过。
class C { C(const T&& X) : x(std::move(X)) {} void set(const T&& X) { x = std::move(X); } T x; };
const T move — does not allow move due to prohibiting change of passed object.
const T move —禁止移动,因为禁止更改传递的对象。
class C { C(const T X) : x(std::move(X)) {} void set(const T X) { x = std::move(X); } T x; };
const Perfect forwarding (PF) — will not compile
const完美转发(PF)-无法编译
class C { template<typename Z> C(const Z&& X) : x(std::forward<Z>(X)) {} template<typename Z> void set(const Z&& X) { x = std::forward<Z>(X); } T x; };
使用智能指针 (Working with smart pointers)
- Do not pass shared_ptr (or other countref alternatives) by value, if you do not need to count references (reduces performance due to atomic operations with counter) — prefer passing objects by * or & as usual. 如果您不需要对引用进行计数(由于使用计数器进行原子操作会降低性能),则不要按值传递shared_ptr(或其他countref替代方法)-像往常一样通过*或&传递对象。
- Do not pass shared_ptr (or other countref alternatives) by reference or const reference, if you do not need to count references — prefer passing objects by * or & as usual. 如果不需要计数引用,请不要通过引用或const引用传递shared_ptr(或其他countref替代方法)-像往常一样,最好通过*或&传递对象。
- If fabric has to return a polymorphic type (and thus cannot return by value), return unique_ptr, which can be converted to shared_ptr if needed (or return shared_ptr if you are sure that it will always be needed intead of unique_ptr) 如果fabric必须返回多态类型(因此无法按值返回),则返回unique_ptr,如果需要,可以将其转换为shared_ptr(或者,如果您确定始终需要unique_ptr,则返回shared_ptr)
- If a function will need to decide if it wants to copy a shared_ptr or not — you can pass a const shared_ptr& to it 如果函数需要决定是否要复制shared_ptr,则可以将const shared_ptr&传递给它
- Never dereference or call a method of non-local shared_ptr, because this puts object outside of shared_ptr control (instead, first make a local copy of shared_ptr). Example: 切勿取消引用或调用非本地shared_ptr的方法,因为这会将对象置于shared_ptr控件之外(相反,请首先创建shared_ptr的本地副本)。 例:
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?
CppCon 2014:Herb Sutter“回到基础!现代C ++风格的基础” PDF演示 本示例中的“别名本地shared_ptr”是什么?
Some of the used images were taken from the linked articles
一些使用的图像取自链接的文章
c&c++语言参考手册