18

この質問では、いつ C++11 でタイプを移動不可にするかについて説明し、Scott Meyers がcomp.std.c++について同様の質問をしていることを発見しました。

  • すべてのミューテックス タイプ (recursive_mutex 、 timed_mutex 、 recursive_timed_mutex 、
  • 条件変数
  • タイプ情報
  • error_category
  • ロケール::ファセット
  • random_device
  • シード_シーケンス
  • 参照_ラッパー
  • 間隔
  • time_point
  • - すべてのイテレータ / イテレータ アダプタ
  • ios_base
  • basic_istream::sentry
  • basic_ostream::sentry
  • すべてのアトミック型
  • once_flag

問題は、なぜall iterators / iterator adaptorsnot-movable なのかということです。

4

4 に答える 4

14

標準が批准される 1 年前の投稿は時代遅れです。ポスターは、活発な委員会メンバーである Daniel Krügler であり、ちょっとした政治的ロビー活動です。

暗黙的に生成された移動操作のルールがピッツバーグの会議で明確になったため、これらは移動可能ではなく、おそらく偶発的なものです。一般的な図書館の問題が開かれました

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1331

ライブラリでの移動サポートの欠如に対処するため。この問題は、FCD に対する国家機関のコメントの十分な代替物ではないことを私は心に留めているので、必ず国家機関の代表者に連絡してください。

言い換えれば、これらすべてのタイプが移動不能であることは、標準にとって重大なバグであり、標準が公式になる前に問題を修正するよう Usenet の読者に要求することを彼は望んでいます。

欠陥は「解決済み」リストに移動されました。解決策は次のとおりです (便宜上提供されているリンク):

仕様のライブラリ部分を確認し、新しく追加されたコア機能 Move Special Member Functions ( N3044 ) を組み込みます。

N3044 はかなりの量の素材であるため、このような基本的な機能が機能するために不可欠である理由は容易に理解できます。

std::durationイテレータやstd::time_point、や のような単純な値セマンティクスを持つものはすべて、確かに移動可能です。他の人が述べたように、コピー可能性は移動可能性を意味し、そうでなければ言語は壊れます。当時、この投稿は間違っていませんでした。むしろ、未完成の言語の壊れやすさについて議論しています。

于 2013-01-14T09:52:42.803 に答える
10

移動できないタイプのリストに「通常のコピーとして移動を実装するクラス」を含めていると思います。. イテレータは、安価にコピーできる軽量オブジェクトと見なされます。彼らのために移動オペレーターを義務付けるのは意味がありません。たとえばstd::vector<T>::iterator、本質的には単にラップされたT*ものであり、それらをコピーすることは移動するのと同じくらい安価です。

于 2013-01-14T07:32:44.950 に答える
2

イテレータに関して「移動できない」とは、おそらく、クラス定義にユーザー宣言の移動操作が含まれていないことを意味します。ただし、イテレータは引き続きコピー可能です。したがって、移動要求は引き続き機能し、コピーにフォールバックします。そのため、移動操作がコピー操作とまったく同じことを行う状況で移動操作を提供する理由はありません。典型的な反復子の実装では、wrt の移動を最適化するものは何もありません。

于 2013-01-14T09:23:08.523 に答える
1

短い答え

コピー可能だからです。

長い答え

まず、「移動」が実際に何を意味するのかを明確にする必要があります。Von Newmanマシンはデータを移動しません:あなたはただ「コピー」します。データはメモリロケーションから別のロケーションにコピーされます。「動かされた」ことはありません。

しかし、より高い抽象化レベルでは、データは他のデータへの単なるポインターになる可能性があります。コピーされたポインターを無効にするポインターをコピーすると、参照されたデータは、ある「所有者」から別の「所有者」に「移動」されたと言われます。

より一般的には、値をコピーし(ポインタに含まれるアドレスをレイクする)、元の値を破棄して認識可能な「無効」に設定する操作は、「移動」と呼ばれます。

C ++に関しては、wheはさまざまなタイプのオブジェクトを区別できます。

  1. 単純な値だけを含むもの。
  2. それらが参照するものを「所有する」単純なポインタまたは参照のみを含むもの
  3. 単純なポインタまたは参照だけを含み、それらが参照するものを「負わない」もの
  4. 巨大な値を含むもの。
  5. 物理エンティティまたはオペレーティングシステム(より一般的な「ホスティングプラットフォーム」)エンティティを表すもの。

このタイプのすべてについて、「コピー」と「移動」の概念は異なる意味を持っている可能性があり、その場合の一部の操作または他の操作はまったく意味を持たない可能性があります。

次に、タイプ1オブジェクトについて考えます。

int a=5; c=0;
c = a;
c = std::move(a);

a引っ越し後の価値は何だと思いますか?どうc = a+bですか?aとbを「移動」する必要がありoperator+ますか?

ここで、タイプ2のオブジェクトについて考えてみます。

std::unique_ptr<int> pa(new int(5)), pb;
pb = std::move(pa);

ここには、2つのスマートポインター(両方ともスコープの終了時に破棄されます)と1つの整数のみがあります。1回だけ実行できる操作(deleteこの場合は、)があるため、整数の「所有権」を保持する必要があるポインターは1つだけです。これは、「コピー」が無意味であり、移動が唯一のサポートされている操作である場合です。

次に、タイプ3のオブジェクトについて考えます。

std::list<int> lst = { 1,2,3,4 };
auto i = lst.begin();
auto j = i; 
*j = *i+5;
++i;
*i = *j;

これは完全に理にかなっています。リストを。にするだけ{ 6,6,3,4 }です。イテレータは、参照するものを所有していません。すべて同じ値を参照しているイテレータが多数存在する可能性があります。コピーは理にかなっていますが、移動は意味がありません。(コピーの代わりに)移動iすると、*iと++iは使用できなくなります。j

次に、タイプ4のオブジェクトについて考えます。

class A
{
   int m[15000000]; //15 million integers
public:
   int& operator[](unsigned x) { return m[x]; }
   const int& operator[](unsigned x) const { return m[x]; }
};

このような巨大な獣は、ほとんどのシステムのスタックに割り当てるのに問題があります。ほとんどの場合、ヒープに残り、(スマート)ポインターによって所有/参照されます。そのアドレスはポインタ間で移動されますが、オブジェクト自体は移動できません。それでもコピー可能である可能性があります。

別の微妙なケースがあります:A自体が動的に割り当てられた巨大な配列へのポインタである場合:これはstd :: vectorと同じです:動的に割り当てられたデータを所有するのはそれ自体が「スマートポインタ」であるため移動可能ですが、所有するデータの新しい別のコピーが必要になる場合があるため、コピー可能にすることもできます。

タイプ5を検討します。

class window
{
private:
   HWND handle;
public:
   window() :handle(CreateWindow(....)) 
   { .... }
   ~window() { DestroyWindow(handle); }
};

ここで、のインスタンスは画面上に存在するウィンドウwindowを表します。「コピー」または「移動」とはどういう意味ですか?

これは、コピーと移動の両方が無効になっている、などmutexの場合に最もよく発生します。condition_variable

于 2013-01-14T08:38:59.660 に答える