web-dev-qa-db-ja.com

C ++ 11がstd :: vector fillコンストラクターのプロトタイプからデフォルト値を削除したのはなぜですか?

C++ 98では、std::vectorのfillコンストラクタのプロトタイプには、初期化子のデフォルト値があります。

explicit vector (size_type n, const value_type& val = value_type(),
                 const allocator_type& alloc = allocator_type());

C++ 11は2つのプロトタイプを使用します。

explicit vector (size_type n);
         vector (size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());

(C++ 14では、フィルコンストラクターが再び変更されましたが、この質問のポイントではありません。)

参照リンクは here です。

C++ 11がデフォルトの初期化子値value_type()を非推奨にしたのはなぜですか?

ところで、私はclang++ -std=c++11を使用して次のコードをコンパイルしようとしましたが、エラーが発生しました。つまり、値型にはS() {}のようなデフォルトコンストラクタが必要です。

#include <vector>

struct S {
    int k;
    S(int k) : k(k) {} // intentionally remove the synthesized default constructor
};

int main() {
    std::vector<S> s(5); // error: no matching constructor
}
43
Leedehai

C++ 98はプロトタイプオブジェクトを取得し、それをn回コピーしました。デフォルトでは、プロトタイプはデフォルトで構築されたオブジェクトでした。

C++ 11バージョンは、デフォルトで構築されたn個のオブジェクトを構築します。

これにより、n個のコピーが削除され、n個のデフォルト構造に置き換えられます。さらに、プロトタイプの構築を回避します。

クラスが次のようになっているとします:

struct bulky {
  std::vector<int> v;
  bulky():v(1000) {} // 1000 ints
  bulky(bulky const&)=default;
  bulky& operator=(bulky const&)=default;

  // in C++11, avoid ever having an empty vector to maintain
  // invariants:
  bulky(bulky&& o):bulky() {
    std::swap(v, o.v);
  }
  bulky& operator=(bulky&& o) {
    std::swap(v,o.v);
    return *this;
  }
};

これはalways1000intsのバッファを所有するクラスです。

次にbulkyのベクトルを作成すると:

std::vector<bulky> v(2);

c ++ 98では、これは1000の整数の3倍を割り当てました。 C++ 11では、これは1000整数の2倍のみを割り当てました。

さらに、C++ 98バージョンでは、型がコピー可能であることが必要です。 C++ 11にはstd::unique_ptr<T>などのコピー不可の型があり、デフォルトで構築された一意のポインターのvectorはC++ 98署名を使用して生成できません。 C++ 11シグネチャには問題ありません。

std::vector<std::unique_ptr<int>> v(100);

C++ 98バージョンがまだある場合、上記は機能しません。

コンストラクターが2つに分割された理由は、unique_ptr<T>などの「移動専用」タイプをサポートするためです。

このコンストラクタ:

vector(size_type n, const T& value, const Allocator& = Allocator());

Tを作成するには、nTsをvalueからコピーする必要があるため、vectorをコピー構築可能にする必要があります。

このコンストラクタ:

explicit vector(size_type n, const Allocator& = Allocator());

doesnotは、Tがコピー構築可能であることを要求します。デフォルトのみ構築可能です。

後者のコンストラクタはunique_ptr<T>で機能します:

std::vector<std::unique_ptr<int>> s(5);

前者のコンストラクタはそうではありません。

この変更を行った提案は次のとおりです。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1858.html#23.2.4.1%20-%20vector% 20コンストラクタ、%20copy、%20and%20assignment

そして、この論文にはいくつかの理論的根拠がありますが、確かに少し簡潔です: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html

Fwiw、resize

void resize(size_type sz, T c = T());

に分割されました:

void resize(size_type sz);
void resize(size_type sz, const T& c);

まったく同じ理由で。 1つ目は、デフォルトの構築可能だがコピー可能な構築物を必要とせず(デフォルトの構築可能な移動専用タイプをサポートするため)、2つ目はコピー構築可能を必要とします。

これらの変更は、100%下位互換性がありませんでした。一部のタイプ(例:参照カウントスマートポインター)では、デフォルトで構築されたオブジェクトからのコピー構築は、デフォルトの構築とは異なります。ただし、移動専用タイプをサポートする利点は、このAPI破損のコストに見合うと判断されました。

47
Howard Hinnant