各C ++開発者はC ++ 11 10のプロパティを使用する必要があります

出典:http://www.ituring.com.cn/article/39533

この記事では、C ++ 11個のすべての開発者の特性を学習し、使用する必要があり、新しいC ++標準、言語と標準ライブラリは、新しいプロパティの多くを追加しているの数を説明し、この記事では、いくつかの毛皮をご紹介しますが、私は、使用量がC ++開発者の毎日の使用の一部になる必要がありますいくつかの機能があると信じています。あなたがC ++ 11件の標準記事を紹介類似した機能の多くを発見したことがあり、この記事では説明されたものの共通の特徴のコレクションとして見ることができます。

内容:

  • autoキーワード
  • nullptrキーワード
  • サイクル間隔に基づいて、
  • オーバーライドし、最終
  • 強く型付けされた列挙
  • スマートポインタ
  • ラムダ式
  • 非会員の始まり()とend()
  • マクロと型抽出static_assert
  • セマンティクスを移動

autoキーワード

C ++ 11標準の前に、自動キーワードは新しい規格で、セマンティック一時的な変数を識別するために使用されます、それは2つの目的のもう一つの目的となります。それは、変数の初期化式の実際の型から推測する必要があるコンパイラを教えてくれる、自動プレースホルダのタイプです。あなたが別のスコープをしたいときに変数を宣言する場合(例えば、名前空間は、関数内で、ループ初期化タイプ用)、オートは、これらの状況で使用することができます。

auto i = 42;        // i is an int
auto l = 42LL;      // l is an long long
auto p = new foo(); // p is a foo*

(あなたはこれが唯一の単語であるint型に必要な場合を除き)、多くの場合、より少ない自動車を意味し、コードの量を使用してください。あなたはSTLコンテナ内の要素をトラバースしたいときは、反復コードを書くだろうかを考え、多くの昔ながらの方法は何をするのtypedefを使用することで、自動プロセスを大幅に簡素化します。

std::map<std::string, std::vector<int>> map;
for(auto it = begin(map); it != end(map); ++it) 
{
}

あなたは、機能として自動戻り値の型とではないに注意する必要がありますが、あなたは、機能を置き換えるために、もちろん、この場合には、関数が値缶を返す必要があり、自動戻り値の型を使用することができます。オートは、関数の戻り値の型の最後の段落で検索するようにコンパイラに指示します戻り値の実際の型を推測するために、コンパイラを教えてくれません。次の例では、関数の戻り値は、型T1とT2型の構成値は、その後、+オペレータの決定を介するものです。

template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double

nullptrキーワード

ヌルポインタ値は、それが整数変数に暗黙的に変換することができるので、このアプローチは、いくつかの欠点を有し、0でした。nullptrキーワード代表値型のstd :: nullptr_t、意味的にヌルポインタと理解することができます。nullptrは、暗黙的に、任意のヌルポインタの種類、およびメンバ関数ポインタと可変ポインタ体に変換することができるが、BOOL(偽の値)に変換することができるが、暗黙的な変換は、変数を整数する状況がもはや存在しません。

void foo(int* p) {}

void bar(std::shared_ptr<int> p) {}

int* p1 = NULL;
int* p2 = nullptr;   
if(p1 == p2)
{
}

foo(nullptr);
bar(nullptr);

bool f = nullptr;
int i = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type

下位互換性のために、値0は、まだヌルポインタとして使用することができます。

サイクル間隔に基づいて、

C ++ 11個の機能は、「foreachの」パラダイムのコレクションをのためのより良いサポートに文の強化します。新バージョンでは、ユーザーがCスタイルの配列の初期化リストを反復処理だけでなく、すべての非メンバーが(開始するために使用)とオーバーロードされた容器を終了することができます。

あなただけの非常に便利なこの形のために、何かをするのではなく、インデックス値、イテレータや要素そのものに集中するコレクション/配列内の要素を取得したいときに。

std::map<std::string, std::vector<int>> map;
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
map["one"] = v;

for(const auto& kvp : map) 
{
  std::cout << kvp.first << std::endl;

  for(auto v : kvp.second)
  {
     std::cout << v << std::endl;
  }
}

int arr[] = {1,2,3,4,5};
for(int& e : arr) 
{
  e = e*e;
}

オーバーライドし、最終

書き換え対象派生クラスで必須の仮想関数を識別するためのメカニズムがないので、私は多くの場合、多くの問題が発生しますC ++での仮想関数を見つけます。virtualキーワードを使用すると、このメソッドは仮想ではないことを確認するために相続のトップレベルに行かなければならないことがあるので、これは、いくつかの困難のコードを読むために増大し、必須ではありません。私は、多くの場合、それは、コードをより読みやすくすることができ、私は同じことをした、派生クラスで仮想キーワードを使用する開発者を奨励します。しかし、そこにいくつかの明らかなエラーがまだ発生し、次のコードは一例です。

class B 
{
public:
   virtual void f(short) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) {std::cout << "D::f" << std::endl;}
};

D ::我々はB :: Fをオーバーライドする必要がありますが、2つが同じ署名機能ではなく、パラメータが短いF、他方がintで、従って、B :: fは単なる及びD :: Fであります同じ機能を命名することは、オーバーロードの代わりに書き換えています。あなたは、ポインタ()のBタイプによってFを呼び出し、D :: Fの出力を楽しみにしてますが、印刷結果は、B :: fとするかもしれません。

別の明白なエラーがあります:パラメータは同じですが、基底クラス内の関数ではなく、派生クラスに比べて、constメンバ関数です。

class B 
{
public:
   virtual void f(int) const {std::cout << "B::f " << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) {std::cout << "D::f" << std::endl;}
};

もう一度、この2つの関数の間の関係は、オーバーロードと書き換えられていないので、あなたは、ポインタのBタイプ()でfを呼び出したい場合は、プログラムではなく、D :: fは、B :: fはプリントアウトされますされています。

幸いなことに、あなたの意図を記述するための方法があり、二つの新しい、特別な識別子(ないキーワード)でのC ++ 11に追加:オーバーライド、あなたは、基本クラスの仮想関数で指定することができなければなりません書き換え、最終的に、ベースクラスの仮想関数をオーバーライドしない派生クラスの関数を指定するために使用することができます。最初の例では、となります。

class B 
{
public:
   virtual void f(short) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) override {std::cout << "D::f" << std::endl;}
};

このコードは(あなたが第二の例を試しオーバーライドの識別子を使用している場合、同じエラーを取得します。)コンパイルエラーがトリガされます。

「D :: F」:関数は、識別子をオーバーライドしており、任意の基底クラス関数をオーバーライドしません

あなたが機能をしたい場合一方、あなたは、基本クラスでは、最終的としての機能を識別することができますし、派生クラスが行うことができます(書き換えされるものではない継承階層下に沿って)に書き換えすることはできません。それが派生クラスである場合は、同時にオーバーライドして、最終的な識別子を使用することができます。

class B 
{
public:
   virtual void f(int) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) override final {std::cout << "D::f" << std::endl;}
};

class F : public D
{
public:
   virtual void f(int) override {std::cout << "F::f" << std::endl;}
};

関数「最終」ステートメントを使用すると、「F :: F」を書き換えることはできません。

強く型付けされた列挙

「伝統的な」C ++列挙型は、いくつかの欠点を有する:それは(列挙型部材をスローした場合に導くことができる2つの列挙型つのコードセクションで同じコード領域内に同じ名前を持つ列挙メンバー)名前の競合を、彼らは整数に暗黙的に変換され、基礎となるデータ型の列挙を指定することはできません。

新しい列挙型を導入することにより、C ++ 11で、これらの問題が解決され、新しい列挙型は強く型付けされた列挙を呼ばれます。このタイプは、それがコードドメインに列挙メンバーを投げたことがないキーワード列挙型クラスを識別するために使用され、それがこの機能も追加されます(暗黙的造形に変換されていませんが、また、ユーザー指定の基になる型を有することができます伝統的な列挙型)。

enum class Options {None, One, All};
Options o = Options::All;

スマートポインタ

スマートポインタを導入しましたので、私はちょうどスマートポインタと参照カウントメモリは自動的に関連する事柄を解放言及したい記事がたくさんがあります。

  • unique_ptrをする:メモリが所有時間は(それがコピーコンストラクタを持っていない)を使用することができる共有されていない場合、しかし、それは別のunique_ptrを(モバイルコンストラクタ)に変換することができます。

  • shared_ptrを:とき共有することができるメモリの一部の所有権を、彼らは(それがその名前と呼ばれる理由である)を使用することができます。

  • weak_ptrを:管理のエンティティオブジェクト参照を指すのshared_ptrを持っていますが、循環参照関係を破るために使用されているすべての作業参照カウントをしませんでした(関係ツリーを想像し、親ノードは、子ノードの基準点(shared_ptrのを)持っています、だけでなく、それは親ノード基準点の子ノードを有していなければならない;第二の基準サイクルに別の参照が解放されませんすることができる任意のオブジェクトを引き起こす、生じる場合)。

つまり、auto_ptrはは廃止され、もはや使用する必要があります。

unique_ptrを使用する場合は、とき所有権のプログラムメモリ要件に応じて、shared_ptrのを使用するために、私はあなたが読むことをお勧めします、ここでの議論を

最初の例は、以下のあなたが別のunique_ptrをに転送するオブジェクトを制御したい場合は、(私は最後の段落で、この機能を説明します)のstd ::動きを使用してください、unique_ptrをの使用方法を示します。スマートポインタの制御がnullになるように制御を転送した後、あなたは、nullptrを返すget()は呼び出した場合。

void foo(int* p)
{
   std::cout << *p << std::endl;
}
std::unique_ptr<int> p1(new int(42));
std::unique_ptr<int> p2 = std::move(p1); // transfer ownership

if(p1)
  foo(p1.get());

(*p2)++;

if(p2)
  foo(p2.get());

第二の例は、shared_ptrの使用を示しています。異なる意味にもかかわらず、所有権を共有しますが、使用方法が似ているからです。

void foo(int* p)
{
}
void bar(std::shared_ptr<int> p)
{
   ++(*p);
}
std::shared_ptr<int> p1(new int(42));
std::shared_ptr<int> p2 = p1;

bar(p1);   
foo(p2.get());

最初の文は、これに相当します。

auto p3 = std::make_shared<int>(42);

非メンバ関数があるmake_shared、メモリは、少なくとも2つのメモリ割り当てを必要とする共有オブジェクト、および割り当てられた唯一のメモリ、およびコンストラクタを明示的に初期化shared_ptrの比較の利点が割り当てられています。種子は()例外をスローした場合、これらの追加費用は、次の例をメモリオーバーフローの問題を引き起こす可能性があり、それはメモリオーバーフローことを示しています。

void foo(std::shared_ptr<int> p, int init)
{
   *p = init;
}
foo(std::shared_ptr<int>(new int(42)), seed());

あなたはmake_shared使用する場合は、同様の問題を回避することができます。第三の例では、あなたがオブジェクトにアクセスするためには、ロック()を呼び出すことによって、オブジェクトへの参照でのshared_ptrを取得する必要があり、注意を払うのweak_ptrの使用を示しています。

auto p = std::make_shared<int>(42);
std::weak_ptr<int> wp = p;

{
  auto sp = wp.lock();
  std::cout << *sp << std::endl;
}

p.reset();

if(wp.expired())
  std::cout << "expired" << std::endl;

すでに期限切れのweak_ptrを(弱参照オブジェクトが解放された)のロックを呼び出すしようとすると、空のshared_ptrを取得します。

ラムダ式

また、ラムダ式として知られている匿名メソッドは、C ++ 11標準年に追加され、すぐに開発者の注目を浴びました。これは非常に強力な機能への関数型言語から抽選で、それは他の機能と強力なライブラリの数を達成することができます。表示された任意の関数オブジェクト、関数、ローカルのstd ::関数の中で、あなたは、あなたができるラムダ式を使用することができますし、ここでラムダの構文をお読みください。

std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);

std::for_each(std::begin(v), std::end(v), [](int n) {std::cout << n << std::endl;});

auto is_odd = [](int n) {return n%2==1;};
auto pos = std::find_if(std::begin(v), std::end(v), is_odd);
if(pos != std::end(v))
  std::cout << *pos << std::endl;

一つは、複雑な再帰的なラムダ式です。あなたは、この自動機能を記述しようとした場合、あなたはコンパイルエラーが発生します、ラムダ式のフィボナッチ機能が表す想像:

auto fib = [&fib](int n) {return n < 2 ? 1 : fib(n-1) + fib(n-2);};

 

error C3533: 'auto &': a parameter cannot have a type that contains 'auto'
error C3531: 'fib': a symbol whose type contains 'auto' must have an initializer
error C3536: 'fib': cannot be used before it is initialized
error C2064: term does not evaluate to a function taking 1 arguments

この問題は、オブジェクト初期化子の種類に応じによる自動車外挿しにあるが、初期化子が、彼自身の表現への参照が含まれているので、まだ循環の問題でその型を、知っておく必要があります。この問題を解決するために、我々は、機能の種類を指定するには、無限ループ、明示的に使用するのstd ::機能を破る必要があります。

std::function<int(int)> lfib = [&lfib](int n) {return n < 2 ? 1 : lfib(n-1) + lfib(n-2);};

非会員の始まり()とend()

あなたは、私がそれ以上にも、非会員は、上記の例で始まる()とSTLに追加された新しい何かがあるエンド()関数で、使用される標準と言語の一貫性を向上させるが、していることに気づいたかもしれません一般的なプログラミングが可能となり、それらのすべては、単にオーバーロードされたSTLコンテナとの互換性はなくはありませんので、あなたは、スプロール(開始することができます)とend()は、Cのために、任意のタイプと互換性があるように配列のヘビーデューティータイプが同じサポートです。

私たちは先に書かれたの例を見てみましょう、この場合には、私はベクトルをプリントアウトし、それを最初の要素の奇数値を見つけることを試みました。Cスタイルの配列内のstd ::ベクトルがそれに取って代わる、そうする場合のコードは次のようになります。

int arr[] = {1,2,3};
std::for_each(&arr[0], &arr[0]+sizeof(arr)/sizeof(arr[0]), [](int n) {std::cout << n << std::endl;});

auto is_odd = [](int n) {return n%2==1;};
auto begin = &arr[0];
auto end = &arr[0]+sizeof(arr)/sizeof(arr[0]);
auto pos = std::find_if(begin, end, is_odd);
if(pos != end)
  std::cout << *pos << std::endl;

あなたが使用している場合、非会員が開始()とend()は、コードを書くことができます。

int arr[] = {1,2,3};
std::for_each(std::begin(arr), std::end(arr), [](int n) {std::cout << n << std::endl;});

auto is_odd = [](int n) {return n%2==1;};
auto pos = std::find_if(std::begin(arr), std::end(arr), is_odd);
if(pos != std::end(arr))
  std::cout << *pos << std::endl;

このコードは、基本的にはstd ::ベクトルと同じであり、この目的のための汎用的な関数を書くタイプの()は、我々はすべてのサポートを開始できることを意味し、コード、()とendを使用しています。

template <typename Iterator>
void bar(Iterator begin, Iterator end) 
{
   std::for_each(begin, end, [](int n) {std::cout << n << std::endl;});

   auto is_odd = [](int n) {return n%2==1;};
   auto pos = std::find_if(begin, end, is_odd);
   if(pos != end)
      std::cout << *pos << std::endl;
}

template <typename C>
void foo(C c)
{
   bar(std::begin(c), std::end(c));
}

template <typename T, size_t N>
void foo(T(&arr)[N])
{
   bar(std::begin(arr), std::end(arr));
}

int arr[] = {1,2,3};
foo(arr);

std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
foo(v);

マクロと型抽出static_assert

static_assertは、アサーションがfalseの場合、コンパイラは、いくつかの特定のエラーメッセージが表示されるだろう、アサーションが真であれば、何も起こりません、アサーションのコンパイラを実行します。

template <typename T, size_t Size>
class Vector
{
   static_assert(Size < 3, "Size is too small");
   T _points[Size];
};

int main()
{
   Vector<int, 16> a1;
   Vector<double, 2> a2;
   return 0;
}

 

error C2338: Size is too small
see reference to class template instantiation 'Vector<T,Size>' being compiled
   with
   [
      T=double,
      Size=2
   ]

で使用され、抽出のタイプは、static_assertがより有用となるであろう場合、これらは、分類の数が存在するクラスをコンパイル時系列が、それらはヘッダファイル内のヘッダ内にカプセル化された追加情報を提供することが可能である:と情報の抽出コンパイルクラス型の種類を取得するために使用される一定のヘルパークラスのコンパイルを作成するには、クラスの型変換の新しいタイプの既存のオーダータイプを変換することができます。

その中に次の例では、機能を追加するだけの基本的なタイプを処理するように設計されています。

template <typename T1, typename T2>
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   return t1 + t2;
}

しかし、あなたがそうであれば、それを書き、エラーがコンパイルされません。

std::cout << add(1, 3.14) << std::endl;
std::cout << add("one", 2) << std::endl;

プログラムは、実際には4.14と「e」を印刷し、私たちはいくつかのコンパイラを追加する場合、以下の2行のコードはコンパイルエラーが生成されますと主張しています。

template <typename T1, typename T2>
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   static_assert(std::is_integral<T1>::value, "Type T1 must be integral");
   static_assert(std::is_integral<T2>::value, "Type T2 must be integral");

   return t1 + t2;
}

 

error C2338: Type T2 must be integral
see reference to function template instantiation 'T2 add<int,double>(T1,T2)' being compiled
   with
   [
      T2=double,
      T1=int
   ]
error C2338: Type T1 must be integral
see reference to function template instantiation 'T1 add<const char*,int>(T1,T2)' being compiled
   with
   [
      T1=const char *,
      T2=int
   ]

セマンティクスを移動

これだけではなく、このトピックに書くことができ、より優れた一連の記事を書き、C ++の多くの技術的な特性に関連する非常に重要と11のトピックです。だから、私はここに、あなたがこのトピックに精通していない場合は、あまりにも多くの技術的な詳細を説明しない、私はいくつかの追加情報をお読みになることをお勧めします。

右の値を指す参照し、参照を区別するために、C ++ 11(&&で表される)、右基準値左側を指す値の概念を導入します。左の値は、オブジェクトの名前であり、正しい値がno-名オブジェクト(一時オブジェクト)です。モバイルセマンティクスは、右の値を変更することができます(その硬直を検討する前に、ひいてはコンセプトのconst T&いくつかの混乱の種類)。

デフォルトコンストラクタ(及び他のコンストラクタを明示的に定義されていない場合のみならば)、コピーコンストラクタ、デストラクタ関数、及び代入演算子のコピー:C ++クラス/構造は、いくつかの暗黙のメンバ関数を有します。コピーコンストラクタと代入演算子コピーは、通常、個別の変数、例えば、ビット単位のコピーをビットごとのコピー(コピーまたは光)を行います。これは、クラスを含むオブジェクトへのポインタを持っている場合、彼らは唯一のアドレスポインタをコピーすることを意味し、オブジェクトポインタをコピーしません。どちらがいくつかのケースでは可能ですが、ほとんどの場合のために、あなたが必要とするすべてのオブジェクトのディープコピーであるあなたが大幅に持っている場合には、むしろ、ポインタそのものの価値よりも、コピーへのポインタでありますタイプは、コピーコンストラクタを書いて、深いコピーを実行するには代入演算子をコピーします。

あなたがデータソースまたは値型をコピーする権利を初期化したいのであれば、(一時的に)どうなりますか?あなたはまだ、その値をコピーする必要がありますが、すぐに、右の値は、メモリの割り当て、およびデータの最後のコピーを含む、いくつかのオーバーヘッドの操作は、これらが不要であることを意味し、消えます。

我々は、これら2つの特別な機能は、T &&パラメータの型の正しい値を受け入れ、モバイルおよびモバイルコンストラクタ代入演算子を導入し、これら二つの機能は、「盗まれた」オブジェクトへの参照のように、オブジェクトを変更することができます。一例として、容器(例えば、ベクターまたはキュー)の特定の実装では、配列要素を指し示すポインタを含むことができる、我々は一時領域と一時的なデータからデータをコピーし、これらの要素のための別のアレイの空間を割り当てることができたときに故障までの時間このメモリを削除し、我々は、インスタンスを直接当中間データを使用することができ、私たちは配列要素へのポインタのアドレスをコピーし、それはオーバーヘッド一度割り当てられたメモリを節約し、一連の要素をコピーして、後に解放されましたオーバーヘッド。

次の例では、このバッファは、名前(単によりよく説明する)、ポインタ(カプセル化とはstd :: unique_ptrを)、Tのポイント型の配列によって識別され、仮想バッファの実装を示しまた、配列変数のメモリサイズを有しています。

template <typename T>
class Buffer 
{
   std::string          _name;
   size_t               _size;
   std::unique_ptr<T[]> _buffer;

public:
   // default constructor
   Buffer():
      _size(16),
      _buffer(new T[16])
   {}

   // constructor
   Buffer(const std::string& name, size_t size):
      _name(name),
      _size(size),
      _buffer(new T[size])
   {}

   // copy constructor
   Buffer(const Buffer& copy):
      _name(copy._name),
      _size(copy._size),
      _buffer(new T[copy._size])
   {
      T* source = copy._buffer.get();
      T* dest = _buffer.get();
      std::copy(source, source + copy._size, dest);
   }

   // copy assignment operator
   Buffer& operator=(const Buffer& copy)
   {
      if(this != ©)
      {
         _name = copy._name;

         if(_size != copy._size)
         {
            _buffer = nullptr;
            _size = copy._size;
            _buffer = _size > 0 > new T[_size] : nullptr;
         }

         T* source = copy._buffer.get();
         T* dest = _buffer.get();
         std::copy(source, source + copy._size, dest);
      }

      return *this;
   }

   // move constructor
   Buffer(Buffer&& temp):
      _name(std::move(temp._name)),
      _size(temp._size),
      _buffer(std::move(temp._buffer))
   {
      temp._buffer = nullptr;
      temp._size = 0;
   }

   // move assignment operator
   Buffer& operator=(Buffer&& temp)
   {
      assert(this != &temp); // assert if this is not a temporary

      _buffer = nullptr;
      _size = temp._size;
      _buffer = std::move(temp._buffer);

      _name = std::move(temp._name);

      temp._buffer = nullptr;
      temp._size = 0;

      return *this;
   }
};

template <typename T>
Buffer<T> getBuffer(const std::string& name) 
{
   Buffer<T> b(name, 128);
   return b;
}
int main()
{
   Buffer<int> b1;
   Buffer<int> b2("buf2", 64);
   Buffer<int> b3 = b2;
   Buffer<int> b4 = getBuffer<int>("buf4");
   b1 = getBuffer<int>("buf5");
   return 0;
}  

デフォルトのコピーコンストラクタとコピー代入演算子はC ++ 11標準に非常によく似ているはずです、新しいものは、モバイルコンストラクタムーブセマンティクスおよびモバイルデザインの代入演算子です。あなたはこのコードを実行する場合は、B4が構築されたときに、コンストラクタは移動を呼び出し、表示されます。B1に値が割り当てられると、動きが代入演算子と呼ばれ、その理由は、()getBufferである右の値が一時的な値である返します。

初期化変数名とバッファへのポインタが、我々はモバイルコンストラクタ内のstd ::動きを使用するときには、詳細を気づいているかもしれません。名前は、私たちが_name(temp._name)を使用する場合、コピーコンストラクタが呼び出されます、しかし、同じunique_ptrをされ、文字列型、のstd ::文字列のサポートムーブセマンティクスの変数ですが、_buffer、このそれがのstd :: unique_ptrをコンストラクタをコピーしないために不可能であるが、なぜオブジェクトはコンストラクタを呼び出すバッファを移動した場合であってもすることは右辺値であるためのstd ::文字列のコンストラクタの動きは?この場合には呼び出されませんコンストラクタの内部で入力しますが、実際に残された値の型であり、その理由は?彼は名前「TEMP」を持っており、オブジェクトの名前は、値の左のタイプですので。それは、再び右の値型(また、右の呼び出しモバイルコンストラクタされるように)なりにするために、我々はSTD ::動きを使用する必要があります。この関数は、ちょうど型基準値の値型に参照を左振る舞います。

アップデート:するために、この例の目的は、移動のコンストラクタを達成し、代入演算子を移動する方法を示すことが、具体的な実装の詳細は変更になる場合がありますし、他のレビューで述べ7805758人のメンバーを達成するための方法であるが我々はより簡単に見るように、私はテキストでそれを書きます。

template <typename T>
class Buffer
{
   std::string          _name;
   size_t               _size;
   std::unique_ptr<T[]> _buffer;

public:
   // constructor
   Buffer(const std::string& name = "", size_t size = 16):
      _name(name),
      _size(size),
      _buffer(size? new T[size] : nullptr)
   {}

   // copy constructor
   Buffer(const Buffer& copy):
      _name(copy._name),
      _size(copy._size),
      _buffer(copy._size? new T[copy._size] : nullptr)
   {
      T* source = copy._buffer.get();
      T* dest = _buffer.get();
      std::copy(source, source + copy._size, dest);
   }

   // copy assignment operator
   Buffer& operator=(Buffer copy)
   {
       swap(*this, copy);
       return *this;
   }

   // move constructor
   Buffer(Buffer&& temp):Buffer()
   {
      swap(*this, temp);
   }

   friend void swap(Buffer& first, Buffer& second) noexcept
   {
       using std::swap;
       swap(first._name  , second._name);
       swap(first._size  , second._size);
       swap(first._buffer, second._buffer);
   }
};

結論

C ++ 11は、上記コンテンツの多くが含まれている初期導入のほんの一部であり、記事の記事は、コア技術のシリーズとC ++標準ライブラリの機能を使用することを示しているが、私は徹底的に、あなたは、少なくともそれらのいくつかのいくつかの余分な機能を行うことをお勧めします読書。

出处:すべてのC ++開発者が使用するテンC ++ 11の機能

 

おすすめ

転載: blog.csdn.net/smartgps2008/article/details/92070340