web-dev-qa-db-ja.com

C ++ 17のstd :: vector推論ガイドとは何ですか?

cppreference の使用によるstd::vectorの控除ガイドについて読みました。

例:

#include <vector>

int main() {
   std::vector<int> v = {1, 2, 3, 4};
   std::vector x{v.begin(), v.end()}; // uses explicit deduction guide
}

だから、私はそれについていくつか質問があります:

  • C++ 17のstd::vector控除ガイドとは何ですか?

  • なぜ、いつベクトル控除が必要なのですか?

  • ここで、xstd::vector<int>またはstd::vector<std::vector<int>>ですか?

23
msc

C++ 17の_std::vector_控除ガイドとは何ですか?

ユーザー定義の推論ガイドを使用すると、ユーザーは、コンストラクター引数からテンプレートクラスの引数を推測する方法を決定できます クラステンプレート引数の推論 。この場合、_std::vector_には、イテレータペアからの構築をより直感的にするための明示的なガイドがあるようです。


なぜ、いつベクトル控除が必要なのですか?

「必要」ではありませんが、ジェネリックコードや非常に明白なコード(つまり、テンプレート引数を明示的に指定しても読者にとって有益ではないコード)では役立ちます


xは_vector<int>_ですか、それとも_vector<vector<int>>_ですか?

これをすばやく理解するための優れたトリックがあります。定義なしでテンプレート関数宣言を記述し、それを呼び出そうとします。コンパイラは、渡された引数の型を出力します。 g ++ 8が出力するものは次のとおりです。

_template <typename> 
void foo();

// ...

foo(x);
_

エラー:_foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ..._の呼び出しに一致する関数がありません

エラーメッセージからわかるように、xは_std::vector<std::vector<int>::iterator>_に推定されます。


どうして?

_std::vector_の控除ガイド cppreference.orgで入手可能 。標準は、イテレータペアからの明示的な演繹ガイドを定義しているようです。

enter image description here

(quoting Rakete1111のように、g ++ 8で発生する動作は正しいようです。

  • オーバーロード解決では、ブレース付き初期化子リストを使用した_std::initializer_list_のコンストラクターが優先されます

  • 他のコンストラクターは、すべての_std::initializer_list_コンストラクターが list-initialization で試行された後にのみ考慮されます。

したがって、_std:vector<std::vector<int>::iterator>_は、リスト初期化を使用する場合の正しい結果です。 実例

std::vector x(v.begin(), v.end())を使用してxを作成する場合、代わりにintが推定されます。 実例

18
Vittorio Romeo

ここで、xは_std::vector<int>_ですか、それとも_std::vector<std::vector<int>>_ですか?

ここでの他の回答はあなたの他の質問に対処しますが、私はこれにもう少し徹底的に対処したいと思いました。クラステンプレートの引数の推論を行うときは、コンストラクターから 関数テンプレートの束を合成 、次に 推論ガイド からさらにいくつかを実行し、オーバーロード解決を実行して正しいものを決定しますテンプレートパラメータ。

_std::vector<T,A>_ にはかなりの数のコンストラクターがありますが、それらのほとんどはTを非推定コンテキストにするTについて言及していません。この過負荷では実行可能なオプションではありません。セットを事前にプルーニングして、実行可能なものだけを使用する場合:

_template <class T> vector<T> __f(size_t, T const& );    // #2
template <class T> vector<T> __f(vector<T> const& );    // #5
template <class T> vector<T> __f(vector<T>&& );         // #6, NB this is an rvalue ref
template <class T> vector<T> __f(initializer_list<T> ); // #8
_

そしてまたこれ 演繹ガイド 、これもアロケーターを削除することで単純化します:

_template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );
_

これらは5つの候補であり、[dcl.init]、__f({v.begin(), v.end()})を介した呼び出しのようにオーバーロードしています。これはリストの初期化であるため、 _initializer_list_候補から開始 であり、存在しない場合にのみ、他の候補に進みます。この場合、実行可能な_initializer_list_候補(#8)があるため、他の候補を考慮せずに選択します。その候補はTを_std::vector<int>::iterator_と推定するため、過負荷解決のプロセスを再開して、_std::vector<std::vector<int>::iterator>_リストのコンストラクターを選択します-2つのイテレーターで初期化されます。

これはおそらく望ましい結果ではありません-おそらく_vector<int>_が必要でした。そこにある解決策は簡単です:_()_ sを使用してください:

_std::vector x(v.begin(), v.end()); // uses explicit deduction guide
_

現在、リストの初期化を行っていないため、_initializer_list_候補は実行可能な候補ではありません。その結果、演繹ガイド(唯一の実行可能な候補)を通じて_vector<int>_を演繹し、そのイテレーターペアコンストラクターを呼び出すことになります。これには、コメントを実際に正しくするという副次的な利点があります。


これは、_{}_での初期化が_()_での初期化とは大きく異なることを行う多くの場所の1つです。 _{}_は均一な初期化であると主張する人もいます-このような例は反論しているようです。私の経験則:_{}_が提供する動作を特に意識的に必要とする場合は、_{}_を使用してください。 _()_それ以外の場合。

7
Barry

C++ 17のstd::vector控除ガイドとは何ですか?

クラステンプレート引数の推定 指定:「クラステンプレートをインスタンス化するには、すべてのテンプレート引数がわかっている必要がありますが、すべてのテンプレート引数を指定する必要はありません。」

そして、それはstd:vectorにローカライズされています。つまり、std:vectorは単なるクラスです。特別なことは何もありません。

これは、参考文献からのstd::vector献身ガイドです。

template< class InputIt,
          class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
vector(InputIt, InputIt, Alloc = Alloc())
  -> vector<typename std::iterator_traits<InputIt>::value_type, Alloc>;

構文に慣れていない場合は、 C++ 17のテンプレート推定ガイドとは何ですか? をお読みください。

なぜ、いつベクトル控除が必要なのですか?

引数からの型の推定がそれらの引数の1つの型に基づいていない場合は、ガイドが必要です。

Xはvector<int>またはvector<vector<int>>ですか?

どちらでもない!

それは:

std::vector<std::vector<int>::iterator>

単純なコンパイルエラーを強制すると(たとえば、xに番号を割り当てることにより)、そのタイプが明らかになります):

error: no match for 'operator=' (operand types are 'std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, std::allocator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > > >' and 'int')

PS:

そのガイドで、なぜその初期化、Alloc = Alloc()が必要なのですか?

これはデフォルト引数であり、アロケーターを渡すことができます。デフォルトでは、2つのガイドは必要ありません。

5
gsamaras