14

「C++ and Beyond 2012: Universal References」のプレゼンテーションで、Scott は繰り返しその点を強調しています。つまり、ユニバーサル参照はすべてを処理/バインドするため、既にユニバーサル参照パラメーターを受け取る関数をオーバーロードすることは意味がありません。と混ざるまでは、疑う理由がありませんでしたstd::initializer_list

以下に短い例を示します。

#include <iostream>
#include <initializer_list>
using namespace std;

template <typename T>
void foo(T&&) { cout << "universal reference" << endl; }

template <typename T>
void foo(initializer_list<T>) { cout << "initializer list" << endl; }

template <typename T>
void goo(T&&) { cout << "universal reference" << endl; }

template <typename T>
void goo(initializer_list<T> const&) { cout << "initializer list" << endl; }

int main(){
    auto il = {4,5,6};
    foo( {1,2,3} );
    foo( il );
    goo( {1,2,3} );
    goo( il );
    return 0;
}

奇妙なことに、VC11 2012 年 11 月 CTP はあいまいさについて不平を言っています ( error C2668: 'foo' : ambiguous call to overloaded function)。さらに驚くべきことは、gcc-4.7.2、gcc-4.9.0、clang-3.4 が次の出力で一致していることです。

initializer list
initializer list
initializer list
universal reference

したがって、(gcc と clang をinitializer_list使用して) s でユニバーサル参照を取る関数をオーバーロードすることは明らかに可能ですが、 - イディオムを使用する場合は、 by 値auto + { expr } => initializer_listを取るか by を取るかが問題になります。少なくとも私にとっては、その行動はまったく驚くべきものでした。標準に準拠している動作はどれですか? その背後にあるロジックを知っている人はいますか?initializer_listconst&

4

3 に答える 3

9

要点は次のとおりです。ブレース初期化リスト ( {expr...}) からの型の推定は、テンプレート引数に対しては機能せず、 のみautoです。テンプレート引数を使用すると、推定エラーが発生し、オーバーロードが考慮から除外されます。これは、最初と 3 番目の出力につながります。

initializer_listby 値を取るか byを取るかは重要ですconst&

foo: 任意の の場合、とパラメータを使用するX2 つのオーバーロードは、左辺値引数に対してあいまいですXX&XX&&

struct X{};
void f(X);
void f(X&);
X x;
f(x); // error: ambiguous overloads

ただし、部分的な順序付け規則がここに介入し (§14.5.6.2)、ジェネリックを受け取る関数は、何かを受け取るジェネリックstd::initializer_listよりも特殊化されています。

goo:パラメータとX&引数を使用する 2 つのオーバーロードの場合、2 番目のオーバーロードではからへの修飾子変換が必要になるため、最初のオーバーロードの方が実行可能です(§13.3.3.1.2/1 表 12 および §13.3.3.2/3 の 3 番目のサブブレット)。 .X const&X&X&X const&

于 2013-06-26T08:19:12.607 に答える
4

スコットが本当に自分が間違っていると言うなら、それは彼が教えている誤解を招く「普遍的な参照」メンタルモデルの別の問題です.

いわゆる「ユニバーサル リファレンス」貪欲であり、望まない場合や期待しない場合でも一致する可能性がありますが、常に最適な一致であるとは限りません。

非テンプレート オーバーロードは完全に一致する可能性があり、「ユニバーサル リファレンス」よりも優先されます。たとえば、これは非テンプレートを選択します。

bool f(int) { return true; }
template<typename T> void f(T&&) { }
bool b = f(0);

また、テンプレート オーバーロードは「ユニバーサル リファレンス」よりも特殊化できるため、オーバーロードの解決によって選択されます。例えば

template<typename T> struct A { };
template<typename T> void f(T&&) { }
template<typename T> bool f(A<T>) { return true; }
bool b = f(A<int>());

DR 1164f(T&)は、evenが左辺値よりも特殊f(T&&)化されており、優先されることを確認しています。

あなたの 2 つのケースでは、オーバーロードはより特殊化されているだけでなく、テンプレート引数推定によって決して推定できないinitializer_listようなブレース初期化リストです。{1,2,3}

結果の説明は次のとおりです。

foo( {1,2,3} );

ブレース初期化リストからテンプレート引数を推定することはできないため、推定は失敗し、実行可能な関数はfoo(T&&)andだけです。foo(initializer_list<int>)

foo( il );

foo(initializer_list<T>)foo(T&&)オーバーロードの解決によって選択されるよりも専門的です。

goo( {1,2,3} );

ブレース初期化リストからテンプレート引数を推測することはできないためgoo(initializer_list<int>)、実行可能な唯一の関数です。

goo( il );

ilは非 const 左辺値であり、deduced asでgoo(T&&)呼び出すことができるため、そのシグネチャは、非 constを const-reference にバインドすることは、非 const-reference にバインドするよりも悪い変換シーケンスであるため、どちらよりも一致します。 .Tinitializer_list<int>&goo(initializer_list<int>&)goo(initializer_list<int> const&)il

上記のコメントの 1 つは、Scott のスライドを次のように引用しています。それは本当です。それこそが、オーバーロードが必要な理由です。特定のタイプにはより具体的な関数が必要で、それ以外のすべてにはユニバーサル参照関数が必要な場合があります。SFINAE を使用してユニバーサル参照関数を制約し、特定の型の処理を停止して、他のオーバーロードがそれらを処理できるようにすることもできます。

標準ライブラリの例として、std::asyncユニバーサル参照を取るオーバーロードされた関数があります。1 つのオーバーロードは最初の引数が型の場合を処理し、もう 1 つのオーバーロードstd::launchはそれ以外のすべてを処理します。SFINAE は、「その他すべて」のオーバーロードがstd::launch、最初の引数として渡される呼び出しに貪欲に一致するのを防ぎます。

于 2013-06-26T12:36:54.253 に答える
0

わかりましたので、まず への反応が理にfooかなっています。initializer_list<T>両方の呼び出しに一致し、より特殊化されているため、この方法で呼び出す必要があります。

の場合goo、これは完全転送と同期しています。を呼び出すとき、 (with ) と定数参照バージョンgoo(il)のどちらかを選択できます。非 const 参照を使用してバージョンを呼び出すことは、const 参照を使用するより特殊なバージョンよりも優先されると思います。そうは言っても、これが標準に関して明確に定義された状況であるかどうかはわかりません。goo(T&&)T = initializer_list<T>&

編集:

テンプレートが存在しない場合は、標準のパラグラフ 13.3.3.2 (暗黙的な変換シーケンスのランキング) によって解決されることに注意してください。ここでの問題は、私の知る限り、テンプレート関数の部分的な順序付けにより、2 番目の (より特殊化された)goo(initializer_list<T> const&)が呼び出されるように指示されますが、暗黙的な変換シーケンスのランキングにより、それが呼び出されるように指示goo(T&&)されます。したがって、これはあいまいなケースだと思います。

于 2013-06-26T08:27:53.110 に答える