web-dev-qa-db-ja.com

`std :: function`と以前に推定されたテンプレートパラメーターによる置換の失敗-なぜですか?

次のコードを検討してください。

_template <typename> 
struct S { };

void g(S<int> t);

template <typename T>
void f(T, std::function<void(S<T>)>);
_

呼び出そうとしたとき

_f(0, g);
_

次のエラーが発生します。

_error: no matching function for call to 'f'
    f(0, g);
    ^

note: candidate template ignored: could not match 
      'function<void (S<type-parameter-0-0>)>' 
      against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
     ^
_

godbolt.orgのライブ例

_std::function_パラメータのタイプは推定されないコンテキストであるため、一般的には推定できないことを理解しています

この場合、Tは最初に渡された引数_0_から推定でき、次にstd::function<void(S<T>)>に代入してstd::function<void(S<int>)>を取得できます。

_T=int_を推定した後、コンパイラーはシグニチャーのすべての場所でTを代入し、引数gを使用して_std::function_パラメーターを構築しようとするでしょう。

そうではないのはなぜですか?置換/演繹が行われる順序はこれと関係があると思いますが、見たいのですが関連する標準的な文言。

ボーナス質問:これは、下位互換性を維持しながら将来の標準で変更される可能性があるものですか、またはこの種の置換が機能しない根本的な理由はありますか?

18
Vittorio Romeo

Std :: functionパラメータのタイプは推定されていないコンテキストであるため、一般的には推定できないことを理解しています。

これは非推論のコンテキストではありません。全く逆です。 std::functionのパラメーターの演繹が試みられますが、引数はstd::functionではないであるため、演繹は失敗します。関数の引数からテンプレートの引数を差し引くと、すべての関数の引数が一致する必要があります。 1つ失敗すると、完全に失敗します。

[temp.deduct.type]

2 場合によっては、タイプPおよびAの単一のセットを使用して演繹が行われます。他の場合には、対応するタイプPおよびAのセットが存在します。タイプの演繹は、各Pに対して独立して行われます/ Aペア、および推定されたテンプレート引数値が結合されます。 P/Aペアで型の推論を実行できない場合、またはいずれかのペアで推論により、推定値の複数のセットが生成される場合、または異なるペアが異なる推定値を生成する場合、またはテンプレート引数が推定値でも明示的にも残っていない場合指定すると、テンプレート引数の推定は失敗します。

2番目の関数パラメーターの型を非推論コンテキストにすることは、実際にはエラーを克服する方法です。

#include <functional>

template<typename T>
struct type_identity {
    using type = T;
};

template <typename> 
struct S { };

void g(S<int> ) {}

template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}

int main() {
    f(0, g);
}

Tは最初の関数引数から正常に推定され、推定するものは何も残っていません。したがって、この献身は成功と見なされます。

ライブ

std::functionパラメータのタイプは、推定されていないコンテキストであるため、一般に推定できないことを理解していますが、この場合、Tは、渡された引数0によって最初に推定できます。

本当じゃない。 Tは、このコンテキストでは推定可能です。コードを次のように変更した場合

template <typename T>
void f(std::function<void(S<T>)>);

int main()
{
    f(std::function<void(S<int>)>(g));
}

コードがコンパイルされ、Tが正しく推定されます。

あなたの問題は、Tを抽出できない関数にオブジェクトを渡そうとしていることです。コンパイラは、Tを推定しようとするときに、関数の引数の変換を行いません。これは、関数に渡される型としてintと関数があることを意味します。 0からintを取得し、2番目のパラメーターで渡したstd::functionから型を取得しようとしますが、std::functionを渡していないため、Tを抽出できません。 、エラーが発生します。

7
NathanOliver