c&c++语言参考手册
The goal of this quick reference is to collect in one place and organize information about value categories in C++, assignment, parameter passing and returning from functions. I tried to make this quick reference convenient to quickly compare and select one of solutions possible, this is why I made several tables here.
此快速参考的目标是在一处收集并组织有关C ++中的值类别,赋值,参数传递和从函数返回的信息。 我试图使此快速参考变得方便,以便快速比较和选择可能的解决方案之一,这就是为什么我在这里制作了几张表。
For introduction to the topic, please use the following links:
有关该主题的介绍,请使用以下链接:
C++ rvalue references and move semantics for beginners Rvalues redefined C++ moves for people who don’t know or care what rvalues are Scott Meyers. Effective Modern C++. 2015 Understanding Move Semantics and Perfect Forwarding: Part 1 Understanding Move Semantics and Perfect Forwarding: Part 2 Understanding Move Semantics and Perfect Forwarding: Part 3 Do we need move and copy assignment
C ++右值引用和初学者的移动语义 Rvalues重新定义了 C ++面向那些不了解或不在乎什么右值是 Scott Meyers的人。 有效的现代C ++。 2015 了解移动语义和完善的转发:第1部分 了解移动语义和完善的转发:第2部分 了解移动语义和完善的转发:第3部分 是否需要移动和复制分配
值类别 (Value categories)
Starting with С++ 11, any expression can belong only to one of three value categories: lvalue, xvalue or prvalue:
从С++ 11开始,任何表达式只能属于以下三个值类别之一:lvalue,xvalue或prvalue:
- xvalue (expiring value) — expression, which identifies a non-temporary object, which can be moved xvalue(到期值)—表达式,它标识可以移动的非临时对象
- lvalue (left value) — expression, which identifies a non-temporary object of function, which cannot be moved lvalue(左值)—表达式,它标识函数的非临时对象,该对象不能移动
- prvalue (pure rvalue) — expression, which initializes an object prvalue(纯右值)—表达式,用于初始化对象
These three categories can be grouped into three overlapping groups:
这三个类别可以分为三个重叠的组:
- glvalue (generalized lvalue) groups lvalue and xvalue. glvalue has address in memory (has identity) and thus usually can be assigned a value (if it is not const) glvalue(广义左值)将左值和左值分组。 glvalue在内存中具有地址(具有标识),因此通常可以分配一个值(如果不是const)
- rvalue groups prvalue and xvalue. rvalue can be either moved (xvalue) or does not belong to an existing object at all (prvalue). rvalue can be passed to move constructors, move assignment operators or move functions 右值将prvalue和xvalue分组。 rvalue可以移动(xvalue)或根本不属于现有对象(prvalue)。 可以将右值传递给移动构造函数,移动赋值运算符或移动函数
So, xvalue has an address in memory and can be moved.
因此,xvalue在内存中有一个地址,可以移动。
You can see from the table above, that named variables of lvalue, lvalue reference and rvalue reference categoties all have the same category of expression: lvalue (highlighted with red). For example, this means, that when rvalue reference is passed to a function, an lvalue reference overload will be chosen: T&& x=T(); f(x);
从上表中可以看到,左值,左值引用和右值引用类别的命名变量都具有相同的表达式类别:左值(以红色突出显示)。 例如,这意味着,当右值引用传递给函数时,将选择左值引用重载: T&& x=T(); f(x); T&& x=T(); f(x);
Links: C++ lvalue rvalue xvalue glvalue prvalue Value categories in C++ 17 Value categories
链接: C ++左值rvalue xvalue glvalue prvalue C ++中的 值类别 17 值类别
本文的缩写 (Abbreviations in this article)
Abbreviations of constructors, operators and destructors:
构造函数,运算符和析构函数的缩写:
- Dc — Default constructor Dc —默认构造函数
- Pc — Parameterized constructor Pc —参数化构造函数
- Cс — Copy constructor Cс—复制构造函数
- Ca — Copy assignment (can reuse allocated storage, e.g. for std::string) Ca —复制分配(可以重复使用分配的存储,例如用于std :: string)
- Mc — Move constructor Mc — Move构造函数
- Ma — Move assignment Ma-搬家
- Va — Conversion assignment operator Va —转换分配运算符
- D — Destructor D —析构函数
Abbreviations of value categories:
值类别的缩写:
LV — Lvalue:
T LV;LV —左值:
T LV;LR — Lvalue reference:
T& LR = LV;LR-左值参考:
T& LR = LV;XV — Xvalue:
move(LV)XV — Xvalue:
move(LV)PRV — Pure rvalue. Literal or result of function call, which return type is not a reference:
T f() { return T(); }PRV —纯右值。 函数调用的文字或结果,返回类型不是引用:
T f() { return T(); }T f() { return T(); }FR — Forwarding reference:
auto&& FR = LV;FR —转发参考:
auto&& FR = LV;- CLV — const LV. CLR — const LR. CXV — const XV. CLV —常量LV。 CLR —常量LR。 CXV —常量XV。
CPRV — const PRV. CPRV exists only for classes and arrays. Non-class non-array CPRV like
cont intwill be implicitly converted to PRV likeint, e.g. in return value of functionconst int f())CPRV —常量PRV。 CPRV仅适用于类和数组。 像
cont int这样的非类非数组CPRV将像int一样隐式转换为PRV,例如在函数const int f()返回值中)- RV is XV or PRV RV是XV或PRV
- CRV is CXV or CPRV CRV是CXV或CPRV
Other abbreviations:
其他缩写:
- PF — Perfect forwarding (using templated forwarding reference to pass parameter to function) PF —完美转发(使用模板化转发引用将参数传递给功能)
分配值类别 (Assigning value categories)
Assigning of value categories can be seen as the basis of all the following sections. When explicitly assigning one category to another without explicit category convertion, an implicit conversion takes place: FROM_TYPE x; TO_TYPE y; y = x; In the table below you can see, what happens when expression of type From is assigned to a variable of type To:
值类别的分配可以视为以下所有部分的基础。 在不进行显式类别转换的情况下将一个类别显式分配给另一类别时,将发生隐式转换: FROM_TYPE x; TO_TYPE y; y = x; FROM_TYPE x; TO_TYPE y; y = x; 在下表中,您可以看到将From类型的表达式分配给To类型的变量时会发生什么:
- A minus sign means that such an assignment is not possible 减号表示不可能进行这种分配
- A plus sign means that such an assignment is possible and it will not require calling of copy/move operators or constructors. 加号表示可以进行这种分配,并且不需要调用复制/移动运算符或构造函数。
- In other cases there is text, which means which copy/move operators and constructors will be called during such an assignment. 在其他情况下,存在文本,这意味着在此类分配期间将调用哪些复制/移动运算符和构造函数。
Conversions, which cannot occur implicitly from returned expression category to function return value type, are marked with red font — please see section "Returning from a function" below for details. Lines with constant types are highlighted with blue.
从返回的表达式类别到函数返回值类型的隐式转换不能用红色字体标记-有关详细信息,请参见下面的“从函数返回”部分。 常量类型的行以蓝色突出显示。
Footnotes:
脚注:
1 — Const auto&& references are not forwarding references, but const rvalue references, so they will act like const T&& 2 — a temporary created to hold a reference initializer persists until the end of its reference’s scope (see next section). Does not work for converting return value to function type. 3 — when passing literal, needs type specifier on the right for auto: auto&& = T{2};
1-const auto &&引用不是转发引用,而是const rvalue引用,因此它们的作用类似于const T && 2-为保存引用初始化程序而创建的临时对象将一直保留到其引用范围的结尾(请参阅下一节)。 无法将返回值转换为函数类型。 3-传递文字时,需要在auto的右侧输入类型说明符: auto&& = T{2};
Note:
注意:
- Non-const lvalue and rvalue references cannot accept any const types. But non-const non-reference type can accept const types. 非常量左值和右值引用不能接受任何常量类型。 但是非const非引用类型可以接受const类型。
- Rvalue reference (T&&) can accept xvalue or prvalue (XV or PRV). 右值引用(T &&)可以接受xvalue或prvalue(XV或PRV)。
- Lvalue reference (T&) can accept lvalue or lvalue reference (LV or LR) 左值参考(T&)可以接受左值或左值参考(LV或LR)
- Non-reference types accept any types by converting, copying, moving or RVO 非引用类型通过转换,复制,移动或RVO接受任何类型
- Const lvalue references (const T&, const auto&) and auto forwarding references (auto&&) accept any types without copy or move const左值引用(const T&,const auto&)和自动转发引用(auto &&)接受任何类型,而无需复制或移动
- Const and non-const rvalue references (T&&, const T&&) cannot accept const or non-const lvalues (LV or CLV). 常量和非常量右值引用(T &&,const T &&)不能接受常量或非常量左值(LV或CLV)。
- LR cannot accept const or non-const rvalue references (XV or PRV or CXV or CPRV). CLR can accept them. LR不能接受常量或非常量右值引用(XV或PRV或CXV或CPRV)。 CLR可以接受它们。
- Types with const modifiers cannot be assigned to any types without const modifiers (except auto, which can become const if const is explicit on the right side). 具有const修饰符的类型不能分配给没有const修饰符的任何类型(auto除外,如果const在右侧是显式的,则可以变为const)。
- Passing prvalue (passing return value, literal or constructor result) is the same as passing xvalue, but avoids calling move constructor when assigning to non-reference type 传递prvalue(传递返回值,文字或构造函数结果)与传递xvalue相同,但在分配给非引用类型时避免调用move构造函数
- Passing const prvalue (passing return value of const type function) is the same as passing const xvalue, but avoids calling copy constructor when assigning to non-reference type 传递const prvalue(传递const类型函数的返回值)与传递const xvalue相同,但避免在分配给非引用类型时调用复制构造函数
- Auto references can become const, so they can accept both const and non-const variants. Also, they can become const references to make temporary persist until the end of reference's scope. But auto * references cannot become const if this is not explicitly specified on the right side. This is why you cannot assign rvalue to auto& 自动引用可以成为const,因此它们可以接受const和非const变体。 而且,它们可以成为const引用,以使临时持久化直到引用范围结束。 但是,如果未在右侧明确指定,则auto *引用将无法成为const。 这就是为什么您不能将rvalue分配给auto&的原因
- Const reference can receive a temporary and make it persist until the end of the reference scope (non-const reference cannot). This works only inside current scope (local function references and function arguments). If reference is a member of a class, temporary object will be destructed at the end of constructor or function and reference will continue pointing to a destructed object, which is undefined behaviour. const引用可以接收一个临时文件,并使它持久直到引用范围的末尾(非const引用不能)。 这仅在当前范围内有效(局部函数引用和函数参数)。 如果reference是类的成员,则临时对象将在构造函数或函数的末尾处被破坏,而引用将继续指向未定义行为的被破坏对象。
Auto x = CLV;will deduce auto to non-const type.Auto x = CLV;将推断出auto为非const类型。
打印每个被调用的构造函数和运算符的示例和测试 (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;
void report(string st) {
if (!from.empty() && !to.empty()) {
res[from][to] += 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 Fprv() { return T(); }
const T Fcprv() { return T(); }
void print_col(const string &st, int width) {
cout << endl << left << setw(width) << st;
}
void test_assign(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 TEST_ASSIGN(t, v) { \
test_assign(#t, #v); \
t s = v; \
cout << s.a; \
UNUSED(s); \
cout << "-"; \
}
void test_conversion() {
T l;
const T cl;
T& lr = l;
const T& clr = l;
T&& rr = T();
const T&& crr = T();
auto &&fr = T();
TEST_ASSIGN(T, 8);
TEST_ASSIGN(T, T());
TEST_ASSIGN(T, l);
TEST_ASSIGN(T, move(l));
TEST_ASSIGN(T, cl);
TEST_ASSIGN(T, move(cl));
TEST_ASSIGN(T, lr);
TEST_ASSIGN(T, move(lr));
TEST_ASSIGN(T, clr);
TEST_ASSIGN(T, move(clr));
TEST_ASSIGN(T, rr);
TEST_ASSIGN(T, move(rr));
TEST_ASSIGN(T, crr);
TEST_ASSIGN(T, move(crr));
TEST_ASSIGN(T, Fcprv());
TEST_ASSIGN(T, Fprv());
TEST_ASSIGN(T, fr);
TEST_ASSIGN(T, move(fr));
TEST_ASSIGN(const T, 8);
TEST_ASSIGN(const T, T());
TEST_ASSIGN(const T, l);
TEST_ASSIGN(const T, move(l));
TEST_ASSIGN(const T, cl);
TEST_ASSIGN(const T, move(cl));
TEST_ASSIGN(const T, lr);
TEST_ASSIGN(const T, move(lr));
TEST_ASSIGN(const T, clr);
TEST_ASSIGN(const T, move(clr));
TEST_ASSIGN(const T, rr);
TEST_ASSIGN(const T, move(rr));
TEST_ASSIGN(const T, crr);
TEST_ASSIGN(const T, move(crr));
TEST_ASSIGN(const T, Fcprv());
TEST_ASSIGN(const T, Fprv());
TEST_ASSIGN(const T, fr);
TEST_ASSIGN(const T, move(fr));
//TEST_ASSIGN(T&, 8);
//TEST_ASSIGN(T&, T());
TEST_ASSIGN(T&, l);
//TEST_ASSIGN(T&, move(l));
//TEST_ASSIGN(T&, cl);
//TEST_ASSIGN(T&, move(cl));
TEST_ASSIGN(T&, lr);
//TEST_ASSIGN(T&, move(lr));
//TEST_ASSIGN(T&, clr);
//TEST_ASSIGN(T&, move(clr));
//TEST_ASSIGN(T&, rr);
//TEST_ASSIGN(T&, move(rr));
//TEST_ASSIGN(T&, crr);
//TEST_ASSIGN(T&, move(crr));
//TEST_ASSIGN(T&, Fcprv());
//TEST_ASSIGN(T&, Fprv());
TEST_ASSIGN(T&, fr);
//TEST_ASSIGN(T&, move(fr));
TEST_ASSIGN(const T&, 8);
TEST_ASSIGN(const T&, T());
TEST_ASSIGN(const T&, l);
TEST_ASSIGN(const T&, move(l));
TEST_ASSIGN(const T&, cl);
TEST_ASSIGN(const T&, move(cl));
TEST_ASSIGN(const T&, lr);
TEST_ASSIGN(const T&, move(lr));
TEST_ASSIGN(const T&, clr);
TEST_ASSIGN(const T&, move(clr));
TEST_ASSIGN(const T&, rr);
TEST_ASSIGN(const T&, move(rr));
TEST_ASSIGN(const T&, crr);
TEST_ASSIGN(const T&, move(crr));
TEST_ASSIGN(const T&, Fcprv());
TEST_ASSIGN(const T&, Fprv());
TEST_ASSIGN(const T&, fr);
TEST_ASSIGN(const T&, move(fr));
TEST_ASSIGN(T&&, 8);
TEST_ASSIGN(T&&, T());
//TEST_ASSIGN(T&&, l);
TEST_ASSIGN(T&&, move(l));
//TEST_ASSIGN(T&&, cl);
//TEST_ASSIGN(T&&, move(cl));
//TEST_ASSIGN(T&&, lr);
TEST_ASSIGN(T&&, move(lr));
//TEST_ASSIGN(T&&, clr);
//TEST_ASSIGN(T&&, move(clr));
//TEST_ASSIGN(T&&, rr);
TEST_ASSIGN(T&&, move(rr));
//TEST_ASSIGN(T&&, crr);
//TEST_ASSIGN(T&&, move(crr));
//TEST_ASSIGN(T&&, Fcprv());
TEST_ASSIGN(T&&, Fprv());
//TEST_ASSIGN(T&&, fr);
TEST_ASSIGN(T&&, move(fr));
TEST_ASSIGN(const T&&, 8);
TEST_ASSIGN(const T&&, T());
//TEST_ASSIGN(const T&&, l);
TEST_ASSIGN(const T&&, move(l));
//TEST_ASSIGN(const T&&, cl);
TEST_ASSIGN(const T&&, move(cl));
//TEST_ASSIGN(const T&&, lr);
TEST_ASSIGN(const T&&, move(lr));
//TEST_ASSIGN(const T&&, clr);
TEST_ASSIGN(const T&&, move(clr));
//TEST_ASSIGN(const T&&, rr);
TEST_ASSIGN(const T&&, move(rr));
//TEST_ASSIGN(const T&&, crr);
TEST_ASSIGN(const T&&, move(crr));
TEST_ASSIGN(const T&&, Fcprv());
TEST_ASSIGN(const T&&, Fprv());
//TEST_ASSIGN(const T&&, fr);
TEST_ASSIGN(const T&&, move(fr));
//TEST_ASSIGN(auto&, T{8});
//TEST_ASSIGN(auto&, T());
TEST_ASSIGN(auto&, l);
//TEST_ASSIGN(auto&, move(l));
TEST_ASSIGN(auto&, cl);
TEST_ASSIGN(auto&, move(cl));
TEST_ASSIGN(auto&, lr);
//TEST_ASSIGN(auto&, move(lr));
TEST_ASSIGN(auto&, clr);
TEST_ASSIGN(auto&, move(clr));
TEST_ASSIGN(auto&, rr);
//TEST_ASSIGN(auto&, move(rr));
TEST_ASSIGN(auto&, crr);
TEST_ASSIGN(auto&, move(crr));
TEST_ASSIGN(auto&, Fcprv());
//TEST_ASSIGN(auto&, Fprv());
TEST_ASSIGN(auto&, fr);
//TEST_ASSIGN(auto&, move(fr));
TEST_ASSIGN(const auto&, T{8});
TEST_ASSIGN(const auto&, T());
TEST_ASSIGN(const auto&, l);
TEST_ASSIGN(const auto&, move(l));
TEST_ASSIGN(const auto&, cl);
TEST_ASSIGN(const auto&, move(cl));
TEST_ASSIGN(const auto&, lr);
TEST_ASSIGN(const auto&, move(lr));
TEST_ASSIGN(const auto&, clr);
TEST_ASSIGN(const auto&, move(clr));
TEST_ASSIGN(const auto&, rr);
TEST_ASSIGN(const auto&, move(rr));
TEST_ASSIGN(const auto&, crr);
TEST_ASSIGN(const auto&, move(crr));
TEST_ASSIGN(const auto&, Fcprv());
TEST_ASSIGN(const auto&, Fprv());
TEST_ASSIGN(const auto&, fr);
TEST_ASSIGN(const auto&, move(fr));
TEST_ASSIGN(auto&&, T{8});
TEST_ASSIGN(auto&&, T());
TEST_ASSIGN(auto&&, l);
TEST_ASSIGN(auto&&, move(l));
TEST_ASSIGN(auto&&, cl);
TEST_ASSIGN(auto&&, move(cl));
TEST_ASSIGN(auto&&, lr);
TEST_ASSIGN(auto&&, move(lr));
TEST_ASSIGN(auto&&, clr);
TEST_ASSIGN(auto&&, move(clr));
TEST_ASSIGN(auto&&, rr);
TEST_ASSIGN(auto&&, move(rr));
TEST_ASSIGN(auto&&, crr);
TEST_ASSIGN(auto&&, move(crr));
TEST_ASSIGN(auto&&, Fcprv());
TEST_ASSIGN(auto&&, Fprv());
TEST_ASSIGN(auto&&, fr);
TEST_ASSIGN(auto&&, move(fr));
TEST_ASSIGN(const auto&&, T{8});
TEST_ASSIGN(const auto&&, T());
//TEST_ASSIGN(const auto&&, l);
TEST_ASSIGN(const auto&&, move(l));
//TEST_ASSIGN(const auto&&, cl);
TEST_ASSIGN(const auto&&, move(cl));
//TEST_ASSIGN(const auto&&, lr);
TEST_ASSIGN(const auto&&, move(lr));
//TEST_ASSIGN(const auto&&, clr);
TEST_ASSIGN(const auto&&, move(clr));
//TEST_ASSIGN(const auto&&, rr);
TEST_ASSIGN(const auto&&, move(rr));
//TEST_ASSIGN(const auto&&, crr);
TEST_ASSIGN(const auto&&, move(crr));
TEST_ASSIGN(const auto&&, Fcprv());
TEST_ASSIGN(const auto&&, Fprv());
//TEST_ASSIGN(const auto&&, fr);
TEST_ASSIGN(const auto&&, move(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;
}
cout << endl;
}
int main() {
test_conversion();
cout << endl;
return 0;
}
/* Output:
Dc Dc Dc Dc Dc
T = 8: Pc 8-D
T = T(): Dc 1-D
T = l: Cc 1-D
T = move(l): Mc 1-D
T = cl: Cc 1-D
T = move(cl): Cc 1-D
T = lr: Cc 0-D
T = move(lr): Mc 0-D
T = clr: Cc 0-D
T = move(clr): Cc 0-D
T = rr: Cc 1-D
T = move(rr): Mc 1-D
T = crr: Cc 1-D
T = move(crr): Cc 1-D
T = Fcprv(): Dc 1-D
T = Fprv(): Dc 1-D
T = fr: Cc 1-D
T = move(fr): Mc 1-D
const T = 8: Pc 8-D
const T = T(): Dc 1-D
const T = l: Cc 0-D
const T = move(l): Mc 0-D
const T = cl: Cc 1-D
const T = move(cl): Cc 1-D
const T = lr: Cc 0-D
const T = move(lr): Mc 0-D
const T = clr: Cc 0-D
const T = move(clr): Cc 0-D
const T = rr: Cc 0-D
const T = move(rr): Mc 0-D
const T = crr: Cc 1-D
const T = move(crr): Cc 1-D
const T = Fcprv(): Dc 1-D
const T = Fprv(): Dc 1-D
const T = fr: Cc 0-D
const T = move(fr): Mc 0-D
T& = l: 0-
T& = lr: 0-
T& = fr: 0-
const T& = 8: Pc 8-D
const T& = T(): Dc 1-D
const T& = l: 0-
const T& = move(l): 0-
const T& = cl: 1-
const T& = move(cl): 1-
const T& = lr: 0-
const T& = move(lr): 0-
const T& = clr: 0-
const T& = move(clr): 0-
const T& = rr: 0-
const T& = move(rr): 0-
const T& = crr: 1-
const T& = move(crr): 1-
const T& = Fcprv(): Dc 1-D
const T& = Fprv(): Dc 1-D
const T& = fr: 0-
const T& = move(fr): 0-
T&& = 8: Pc 8-D
T&& = T(): Dc 1-D
T&& = move(l): 0-
T&& = move(lr): 0-
T&& = move(rr): 0-
T&& = Fprv(): Dc 1-D
T&& = move(fr): 0-
const T&& = 8: Pc 8-D
const T&& = T(): Dc 1-D
const T&& = move(l): 0-
const T&& = move(cl): 1-
const T&& = move(lr): 0-
const T&& = move(clr): 0-
const T&& = move(rr): 0-
const T&& = move(crr): 1-
const T&& = Fcprv(): Dc 1-D
const T&& = Fprv(): Dc 1-D
const T&& = move(fr): 0-
auto& = l: 0-
auto& = cl: 1-
auto& = move(cl): 1-
auto& = lr: 0-
auto& = clr: 0-
auto& = move(clr): 0-
auto& = rr: 0-
auto& = crr: 1-
auto& = move(crr): 1-
auto& = Fcprv(): Dc 1-D
auto& = fr: 0-
const auto& = T{8}: Pc 8-D
const auto& = T(): Dc 1-D
const auto& = l: 0-
const auto& = move(l): 0-
const auto& = cl: 1-
const auto& = move(cl): 1-
const auto& = lr: 0-
const auto& = move(lr): 0-
const auto& = clr: 0-
const auto& = move(clr): 0-
const auto& = rr: 0-
const auto& = move(rr): 0-
const auto& = crr: 1-
const auto& = move(crr): 1-
const auto& = Fcprv(): Dc 1-D
const auto& = Fprv(): Dc 1-D
const auto& = fr: 0-
const auto& = move(fr): 0-
auto&& = T{8}: Pc 8-D
auto&& = T(): Dc 1-D
auto&& = l: 0-
auto&& = move(l): 0-
auto&& = cl: 1-
auto&& = move(cl): 1-
auto&& = lr: 0-
auto&& = move(lr): 0-
auto&& = clr: 0-
auto&& = move(clr): 0-
auto&& = rr: 0-
auto&& = move(rr): 0-
auto&& = crr: 1-
auto&& = move(crr): 1-
auto&& = Fcprv(): Dc 1-D
auto&& = Fprv(): Dc 1-D
auto&& = fr: 0-
auto&& = move(fr): 0-
const auto&& = T{8}: Pc 8-D
const auto&& = T(): Dc 1-D
const auto&& = move(l): 0-
const auto&& = move(cl): 1-
const auto&& = move(lr): 0-
const auto&& = move(clr): 0-
const auto&& = move(rr): 0-
const auto&& = move(crr): 1-
const auto&& = Fcprv(): Dc 1-D
const auto&& = Fprv(): Dc 1-D
const auto&& = move(fr): 0-
From: T const T T& const T& T&& const T&&auto& const auto&auto&& const auto&&
8 PcD PcD - PcD PcD PcD - - - -
T() DcD DcD - DcD DcD DcD - DcD DcD DcD
l CcD CcD + + - - + + + -
move(l) McD McD - + + + - + + +
cl CcD CcD - + - - + + + -
move(cl) CcD CcD - + - + + + + +
lr CcD CcD + + - - + + + -
move(lr) McD McD - + + + - + + +
clr CcD CcD - + - - + + + -
move(clr)CcD CcD - + - + + + + +
rr CcD CcD - + - - + + + -
move(rr) McD McD - + + + - + + +
crr CcD CcD - + - - + + + -
move(crr)CcD CcD - + - + + + + +
Fcprv() DcD DcD - DcD - DcD DcD DcD DcD DcD
Fprv() DcD DcD - DcD DcD DcD - DcD DcD DcD
fr CcD CcD + + - - + + + -
move(fr) McD McD - + + + - + + +
T{8} - - - - - - - PcD PcD PcD
D D D D D
*/
用临时对象初始化常量引用 (Initializing constant references with temporary objects)
C++ allows to initialize a constant reference with a temporary object. In this case lifetime of a temporary object will be extended. Example:
C ++允许使用临时对象初始化常量引用。 在这种情况下,将延长临时对象的寿命。 例:
struct T {
int i = 1;
};
const T& t = T();
cout << t.i;
Yet, this lifetime extension works only up to the end of the block, where temporary object was created. For this reason, if a constant reference member of a class in initialized with a temporary object in a constructor, temporary object will be destructed at the end of constructor and reference will continue to point to a destructed object, which is undefined behavior:
但是,此生命周期扩展仅在创建临时对象的块末尾有效。 因此,如果在构造函数中用临时对象初始化了类的常量引用成员,则临时对象将在构造函数的结尾处被破坏,并且引用将继续指向被破坏的对象,这是未定义的行为:
class A {
public:
// Will not compile: value-initialization of reference type
//A() : t() {}
const T& t;
};
class B {
public:
// Will compile in some compilers, but temporary object will be destructed at the end of constructor
B() : t(T()) {
cout << "In constructor: " << t.i << endl;
}
const T& t;
};
class C {
public:
// Will compile, but temporary object will be destructed at the end of constructor
// Address sanitizer will show the problem
C() : t(std::move(T())) {
cout << "In constructor: " << t.i << endl;
}
const T& t;
};
C c;
cout << "C: " << c.t.i << endl;
Without address sanitizer this program will output some garbage, and with address sanitizer an error will be shown. For this reason thi C++ feature should not be used or should be used with caution.
没有地址清理器,该程序将输出一些垃圾,而使用地址清理器,将显示错误。 因此,不应使用C ++功能或应谨慎使用。
Links: Reference initialization Const References to Temporary Objects About binding a const reference to a sub-object of a temporary
链接: 参考初始化 const对临时对象的 引用关于将const引用绑定 到临时对象 的子对象
c&c++语言参考手册
本文深入探讨C++中的值类别概念,包括lvalue、xvalue和prvalue,以及它们在赋值、参数传递和函数返回中的作用。通过实例展示了不同值类别之间的转换过程,特别关注移动语义在提高性能方面的重要性。
553

被折叠的 条评论
为什么被折叠?



