1.左の値と右の値
値のアドレスを取ることができますことは、左、または右に値されます
int foo() {
return 0;
}
int a = 1; //a左值, &a有效
int &&b = 1; //b左值, &b有效
a + 1; //a + 1右值,&(a+1)无效
foo(); //foo()右值, &(foo())无效
1; //右值, &1无效
int c = b + a;//a, b, c都是左值,不要以出现在等号左右来区分左值右值
2.左側の基準値、左に一定基準の値、名前の右辺値参照、名前右辺値参照
左基準値、右辺値参照は名前の左側に定数の参照、の値は、無名の右辺値参照:4つの参照があります。
- 左基準値T&:左辺値参照するメモリアドレスのニーズは、それゆえ、唯一のバインド値が左にすることができます。
- T&constの定数左辺値参照:一定の左辺値参照値は、左または右の値をバインドすることができます。
- 変数名の値を参照すると、右、できるだけバインド正しい値:右辺値参照T &&名前付き。
- 名前右辺値参照ははstd ::移動(T):変数名が値が左または右の値をバインドすることはできません、右基準値ではありませんが。
できるバインド | 左辺値 | 右辺値 |
---|---|---|
左基準値 | はい | ノー |
左一定の基準値 | はい | はい |
名前付き右辺値参照 | ノー | はい |
名前右辺値参照 | ノー | ノー |
int foo() {
return 0;
}
int a = 0;
int &b = a; //可以, 左值引用绑定左值
int &c = 1; //不行,左值引用不能绑定右值
int &d = foo(); //不行,左值引用不能绑定右值
const int &e = a; //可以,常量左值引用可以绑定左值
const int &f = foo(); //可以,常量左值引用可以绑定右值
int && g = b; //不可以,具名右值引用不可以绑定左值
int && h = foo(); //可以,具名右值引用绑定右值
int && i = a + a; //可以,具名右值引用绑定右值
std::move(a) = 1; //不可以,无名右值引用不能绑定左值或者右值
int && j = std::move(a);//可以,具名右值引用绑定无名右值引用
左基準値、左に定数参照の値、および名前の右辺値参照の三つの変数の型であり、int型、char型、値が残っている限り、同じアドレスを持っています。無名の右辺値参照にはアドレスは、左の値に属していません。
実装では、と引用された左の値が同じではない、無名の右辺値参照は、そのメモリ参照の内容のためのスペースを開きます。このとき、基準値は、コンテンツアドレスのために必要と右側空間と命名される。例えば、以下の例のように(x64の8バイト)+コンテンツサイズは、スタック上の前、12バイト、4バイトの記憶領域(INT)1を開きます8バイトのアドレスに格納されたコンテンツ
int main() {
int &&a = 1;
return 0;
}
これは、コンパイルされています
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $1, %eax
movl %eax, -12(%rbp) ; [rbp - 12 , rbp - 8) <-- (int)1
leaq -12(%rbp), %rax ; 取地址,rax = rbp - 12
movq %rax, -8(%rbp) ; 存地址 [rbp - 8, rbp) <--- rbp - 12
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
したがって、我々は最初の変数、次の出力例2に適用しなければならない左と同じ値を参照せずに、名前右辺値参照の内容を変更することができ
#include <iostream>
int foo() {
return 0;
}
int main() {
int &&a = foo();
a = 2;
std::cout<<a<<std::endl;
}
だから、右辺値参照を名前の操作では、我々は右の値を変更することができ、参照自体の値は、直接右で命名変更することができ、あなたは他の変更がT&T = &&方法によって参照させることができます
3.のstd ::移動
左または右の値が値無名の右辺値参照となりますためのstd ::動きが担当しています。移動の実装を初めて目:
template <class typename>
struct remove_reference { typedef T type; };
template <class typename>
struct remove_reference<T&> { typedef T type; };
template <class typename>
struct remove_reference<T&&> { typedef T type; };
template<typename T>
typename std::remove_reference<T>::type&& move(T&& t) {
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
STD ::このようINT&int型への変更などの型への参照を削除する責任があるremove_reference、
私たちは、参照右の入力は名前の基準値で移動し、出力は名前の右辺値参照であることができます。Oの違いは何ですか?T自体は、非基準intとして、入力されてもよい、はstd ::文字列、また、スタンダード::文字列と、INTを&&引用し、出力のみ未知の右辺値参照であることができます。この変更を理解するために、最初に次のコードをエンドTに入力を検出することはどのようなタイプであります
#include <iostream>
#include <string>
template<typename T>
bool T_is_value(T && x) {
using U = typename std::remove_reference<T>::type;
return std::is_same<T, U>::value && (!std::is_const<U>::value);
}
template<typename T>
bool T_is_const_value(T && x) {
using U = typename std::remove_reference<T>::type;
return std::is_same<T, U>::value && std::is_const<U>::value;
}
template<typename T>
bool T_is_left_ref(T && x) {
using U = typename std::remove_reference<T>::type&;
using V = typename std::remove_reference<T>::type;
return std::is_same<T, U>::value && (!std::is_const<V>::value);
}
template<typename T>
bool T_is_const_left_ref(T && x) {
using U = typename std::remove_reference<T>::type&;
using V = typename std::remove_reference<T>::type;
return std::is_same<T, U>::value && std::is_const<V>::value;
}
template<typename T>
bool T_is_right_ref(T && x) {
using U = typename std::remove_reference<T>::type&&;
using V = typename std::remove_reference<T>::type;
return std::is_same<T, U>::value && (!std::is_const<V>::value);
}
template<typename T>
bool T_is_const_right_ref(T && x) {
using U = typename std::remove_reference<T>::type&&;
using V = typename std::remove_reference<T>::type;
return std::is_same<T, U>::value && std::is_const<V>::value;
}
class A {};
int main () {
const A a;
std::cout<<"T_is_value "<<T_is_value(a)<<std::endl;
std::cout<<"T_is_const_value "<<T_is_const_value(a)<<std::endl;
std::cout<<"T_is_left_ref "<<T_is_left_ref(a)<<std::endl;
std::cout<<"T_is_left_const_ref "<<T_is_const_left_ref(a)<<std::endl;
std::cout<<"T_is_right_ref "<<T_is_right_ref(a)<<std::endl;
std::cout<<"T_is_const_right_ref "<<T_is_const_right_ref(a)<<std::endl;
return 0;
}
輸出
T_is_value 0
T_is_const_value 0
T_is_left_ref 0
T_is_left_const_ref 1
T_is_right_ref 0
T_is_const_right_ref 0
従ってTは、A&const参照型T左定数値は=です。エントリサブルーチンパラメータは、実際の入力引数がCONST A&&&であるので、コンパイラは、このタイプの取得方法であり、右辺値参照型T &&と命名されているので?まず、基準以下のC ++参照の崩壊:
T && & = T &
T& && = T &
T && && = T &&
Tのconstが持ついずれかの揮発性であることができます
ここでは例を見てです:
- A const型、T =定数Aは、次いで、パラメータがとして渡された場合:CONST A && T =、許可されていない、左側の値は、正しい値が指定された参照に結合することができないからです。
- その後、A&&& T = A CONST引数として渡されたA&、もしT =定数、前の基準崩壊規則に従って、左側に結合され、左側の基準値のCONST A&T = A、一定の値を有します。
- もしT =定数A &&、崩壊ルールによって得られるのconst A && T = A、左命名基準結合値の正しい値、不可能です。
だから、コンパイラは2番目のオプションを使用しています
- 左に基準値の場合、上記メイン関数:B; A&CONST = B、次いでT =定数A&
- 変更STDの上記主な機能であれば::移動()、その後、T =定数A
注無名の右辺値参照に予約のconst /揮発この機能、機能は持っているn個のパラメータは、それぞれがオーバーライドのconst /非const参照にする必要がありますとき、私たちはこれらの2つのタイプを統一する&& Tを使用することができます
4のstd ::前方
上記の例から、我々は、関数のパラメータの値が常に残されているので、参照のことなので、関数呼び出しの後、無名の右辺値は姿を消しました。次のコードを考えてみましょう
#include <iostream>
template <typename T>
void print(T &t) {
std::cout<<"T &t"<<std::endl;
}
template <typename T>
void print(const T &t) {
std::cout<<"const T &t"<<std::endl;
}
template <typename T>
void print(T &&t) {
std::cout<<"T &&t"<<std::endl;
}
template <typename T>
void forward_value(T&& val) {
print(val);
}
class A {};
int main () {
A a;
forward_value(a);
forward_value(std::move(a));
return 0;
}
輸出
T &t
T &t
この機能を無効印刷(T && t)を呼び出すことができず、これを変更する方法?
std ::前方にこの問題を解決するために、コードは、以下の変更を行います。
template <typename T>
void forward_value(T&& val) {
print(std::forward<T>(val));
}
出力
T &t
T &&t
STDは::前方にこれを行う方法ですか?実装を見てください
template<typename T>
T&& forward(typename std::remove_reference<T>::type& t) {
return static_cast<T&&>(t);
}
template<typename T>
T&& forward(typename std::remove_reference<T>::type&& t) {
static_assert(!std::is_lvalue_reference<T>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<T&&>(t);
明らかに、つまり、Tは、再び電源を入れ&&。上記の例を考えてみましょう、
- forward_value()。=>のstd ::フォワード<A &>(ヴァル))。=>リターンはstatic_cast <T&&&>(T)。=>印刷(A&)
- forward_value(STD ::移動()); =>のstd ::フォワード(ヴァル))。=>リターンはstatic_cast <T &&>(T)。=>印刷(A &&)