web-dev-qa-db-fra.com

Échec de substitution avec `std :: function` et le paramètre de modèle précédemment déduit - pourquoi?

Considérez le code suivant:

template <typename> 
struct S { };

void g(S<int> t);

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

Lorsque vous tentez d'invoquer

f(0, g);

J'obtiens l'erreur suivante:

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>)>);
     ^

exemple en direct sur godbolt.org

Bien que je comprenne qu'en général, le type du paramètre std::function Ne peut pas être déduit car il s'agit d'un contexte non déduit

Dans ce cas, T peut d'abord être déduit par l'argument passé 0, Puis remplacé par std::function<void(S<T>)> pour obtenir std::function<void(S<int>)>.

Je m'attendrais à ce qu'après déduction de T=int, Le compilateur substituerait T partout dans la signature, puis tenterait de construire le paramètre std::function Avec l'argument g.

Pourquoi n'est-ce pas le cas? Je suppose que l'ordre dans lequel la substitution/déduction se produit a quelque chose à voir avec cela, mais j'aimerais voir le libellé standard correspondant.

Question bonus: est-ce quelque chose qui pourrait potentiellement être modifié dans une future norme tout en préservant la compatibilité descendante, ou y a-t-il une raison fondamentale pour laquelle ce type de substitution ne fonctionne pas?

18
Vittorio Romeo

Bien que je comprenne qu'en général, le type du paramètre std :: function ne peut pas être déduit car il s'agit d'un contexte non déduit.

Ce n'est pas un contexte non déduit. Bien au contraire. Parce que la déduction pour le paramètre de std::function est tenté, mais l'argument est pas a std::function, la déduction échoue. La déduction des arguments de modèle des arguments de fonction doit correspondre à tous les arguments de fonction. S'il échoue pour un, il échoue complètement.

[temp.deduct.type]

2 Dans certains cas, la déduction se fait en utilisant un seul ensemble de types P et A, dans d'autres cas, il y aura un ensemble de types correspondants P et A. La déduction de type se fait indépendamment pour chaque P/Une paire et les valeurs d'argument de modèle déduites sont ensuite combinées. Si la déduction de type ne peut pas être effectuée pour une paire P/A, ou si pour n'importe quelle paire la déduction conduit à plus d'un ensemble possible de valeurs déduites, ou si différentes paires produisent des valeurs déduites différentes, ou si aucun argument de modèle ne reste ni déduit ni explicitement spécifié, la déduction des arguments du modèle échoue.

Faire le type du deuxième paramètre de fonction dans un contexte non déduit est en fait la façon dont on peut surmonter l'erreur.

#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 est déduit avec succès du premier argument de fonction, et il n'y a plus rien à déduire. La déduction est donc considérée comme un succès.

en direct

Bien que je comprenne que, généralement, le type de std::function le paramètre ne peut pas être déduit car il s'agit d'un contexte non déduit, dans ce cas T peut d'abord être déduit par l'argument passé 0.

Ce n'est pas vrai. T est déductible dans ce contexte. Si vous changez le code en

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

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

le code compilerait et T est correctement déduit.

Votre problème est que vous passez un objet à la fonction dont il ne peut pas extraire T. Le compilateur n'effectue aucune conversion des arguments de la fonction lorsqu'il essaie de déduire T. Cela signifie que vous avez un int et une fonction en tant que types passés à la fonction. Il obtient int de 0, puis essaie d'obtenir le type à partir de std::function vous passez le deuxième paramètre mais puisque vous n'avez pas passé un std::function il ne peut pas extraire T et à cause de cela, vous obtenez une erreur.

7
NathanOliver