『c++11を深く応用する』第二章

9457 ワード

1、c++の左と右を区別する左は、式が終了しても存在する永続オブジェクトであり、右は式が終了しても存在しない一時オブジェクトである.最も簡単な区別方法は、式にアドレスを取ることができるのは左値で、右値の右値=亡き値+純右値ではありません.
2.定数の左値参照は「万能」参照タイプで、左、右、定数の左、定数の右の値を使用できます.
3、自動タイプ導出が発生した場合(例えば関数テンプレートのタイプ自動導出、またはautoキーワード)、&&のみがuniversal referencesを参照します.
template void f(constr T&&& param); このparamも実は右値参照で、汎用参照はT&&の下でのみ発生し、いかなる追加条件も失効させ、普通の右値参照になります.
注意区分:共通参照は初期化されなければなりません.左の値か右の値かは初期化に依存し、左の値で初期化されると左の値になります.逆に右の値です.これは実は引用折りたたみです.T&&という未定の参照タイプは、パラメータとして左または右の参照のパラメータによって初期化される可能性があり、それ自体のタイプが変化します.1)すべての右値参照が右値参照に重畳され、依然として右値参照2)他のすべての参照タイプ間の重畳が左値参照に変わります.
また、T&&をパラメータとする場合、Tのタイプi)T&&&をテンプレートパラメータとする場合、左値Xで初期化されると、TのタイプはX&となり、右値Xで初期化されると、TのタイプはXとなる
4.コンパイラは、名前付きの右の値の参照を左、名前なしの右の値を右とします.
void PrintValue(int& i)
{
    std::cout<<"lvalue:"<std::endl;
}

void PrintValue(int&& i)
{
    std::cout<<"rvalue:"<std::endl;
}

void Forward(int&& i)
{
    PrintValue(i);
}
int main(int argc, char **argv) {
    int i=0;
    PrintValue(i);
    PrintValue(1);
    Forward(2);
}

結果:
lvalue:0
rvalue:1
lvalue:2

Forwardが転送したのは右の値ですが、PrintValueの時にまた左の値になりました.
5,c++11の新しい強いタイプの列挙について
/** *“  ” C++         : *                   (                            ,        ), *            ,                。 */
    enum Status1{Ok, Error};

    //enum Status2{Ok, Error}; // Err,       , Status1      Ok, Error



    //  C++11 ,            
    enum class StatusN1 {Ok, Error};

    enum struct StatusN2 {Ok, Error};


    //StatusN1 flagN1 = Ok; // err,          
    StatusN2 flagN2 = StatusN2::Ok;



    enum class C : char {C1 = 1, C2 = 2}; //            
    enum class D : unsigned int { D1 = 1, D2 = 2, Dbig = 0xFFFFFFF0U };

    std::cout << sizeof(C::C1) << std::endl; // 1
    std::cout << sizeof(D::D1) << std::endl; // 4
    std::cout << sizeof(D::Dbig) << std::endl; // 4

6.スタックメモリを含むクラスについては、深いコピーのコピー構造関数を向上させる必要がある
7は、一般的には、移動が成功しない場合にコピーを防止し、コードのセキュリティを向上させるために、移動構造関数を提供すると同時にコピー構造関数を提供する.
8,std::moveメソッドは左値を右値に変換し、意味の移動を容易にします.moveは、所有権を1つのオブジェクトから別のオブジェクトに移動します.ただし、メモリコピーはありません.左値を右値参照に変更し、move意味呼び出しコンストラクション関数を適用してコピーを回避します.moveは、メモリ、ファイルハンドルなどのリソースを持つメンバーのオブジェクトに有効です.intやchar[10]配列などの基本タイプの場合、moveを適用すると、対応する移動構造関数がないため、コピーが発生します.
class A
{
public:
A():m_ptr(new int(0))
{
    std::cout<<"construct"<<std::endl;
}

A(const A& a):m_ptr(new int(*a.m_ptr))
{
    std::cout<<"copy construct"<<std::endl;
}

A(A&& a):m_ptr(a.m_ptr)
{
    a.m_ptr = nullptr;
    std::cout<<"move construct"<<std::endl;
}

~A()
{
    std::cout<<"destruct"<<std::endl;
    delete m_ptr;
}

private:
    int* m_ptr;
};

A get(int flag)
{
    A a;
    A b;
if(flag)
return a;
else
return b;
}

int main(int argc, char **argv) {
    static const int& flag=0;
    A a=get(flag);
}

結果:
construct
construct
move construct
destruct
destruct
destruct

コピーコンストラクタが呼び出されていないことがわかります.移動コンストラクタが呼び出されます.
9,forwardは完璧な転送と記憶して右値参照aは右値オブジェクトをバインドした左値1つの右値参照タイプを関数のパラメータとして用い,関数内部でこのパラメータを再転送すると左値になり,元のタイプを失う.パーフェクト転送:関数テンプレートでは、テンプレートのパラメータのタイプ(パラメータの左右値属性を保持)に完全に依存し、関数テンプレートで呼び出された別の関数にパラメータを渡します.
void PrintT(int& t)
{
    cout<<"lvalue"<template<typename T>
void PrintT(T && t)
{
    cout<<"rvalue"<template<typename T>
void TestForward(T && v)
{
    PrintT(v);
    PrintT(std::forward(v));
    PrintT(std::move(v));
}

int main(int argc, char **argv) {
    TestForward(1);
    int x=1;
    TestForward(x);
    TestForward(std::forward<int>(x));

    return 0;
}

結果:
lvalue
rvalue
rvalue
lvalue
lvalue
rvalue
lvalue
rvalue
rvalue

10, emplace_backメモリコピーの削減とemplaceの移動backはパラメータによってオブジェクトをその場で構築することができ、メモリのコピーや移動を必要とせず、通常empalce_を優先的に使用します.pushの代わりにbackback. でもemplace_bakeはオブジェクトに対応するコンストラクション関数があることを要求し、なければエラーを報告します.
struct A
{
public:
    int x;
    double y;
// /A(int a,double b):x(a),y(b){}
};

int main(int argc, char **argv) {
    vector v;
    v.emplace_back(1,2);
    cout<return 0;
}

明示的なコンストラクション関数を削除するとerrorが報告されますが、正常に動作しvectorの長さを1にすることができます.やはり、明示的なコンストラクション関数がある場合はemplace_を使用することをお勧めします.backコンテナに要素を追加します.
11,秩序化容器の内部は赤と黒の木で,要素が挿入されたときに自動的に並べ替えられ,無秩序容器の内部はhash表である.