Pourquoi ne pas trouver ADL modèles de fonction?
-
22-10-2019 - |
Question
Quelle partie de la personne à charge C ++ spécification argument restreint recherche de trouver des modèles de fonction dans l'ensemble des espaces de noms associés? En d'autres termes, pourquoi le dernier appel en main
ci-dessous ne parviennent pas à compiler?
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
void non_template(foo const&) {}
}
int main() {
ns::foo f;
non_template(f); // This is fine.
frob<0>(f); // This is not.
}
La solution
Cette partie explique:
C ++ standard 03 14.8.1.6 :
[Note: Pour les noms simples de fonction, l'argument recherche dépendante (3.4.2) applique même lorsque le nom de la fonction ne soit pas visible dans le cadre de l'appel. En effet, l'appel a encore la forme syntaxique d'un appel de fonction (3.4.1). Mais quand un modèle de fonction avec des arguments de modèle explicite est utilisé, l'appel n'a pas la forme syntaxique correcte à moins d'un modèle de fonction avec ce nom visible au point de l'appel. Si un tel nom est visible, l'appel n'a pas syntaxiquement bien formée et recherche dépendante-argument ne vaut pas. Si un nom tel est visible, la recherche dépend des arguments et applique des modèles de fonction supplémentaires peuvent être trouvés dans d'autres espaces de noms.
namespace A {
struct B { };
template<int X> void f(B);
}
namespace C {
template<class T> void f(T t);
}
void g(A::B b) {
f<3>(b); //ill-formed: not a function call
A::f<3>(b); //well-formed
C::f<3>(b); //ill-formed; argument dependent lookup
// applies only to unqualified names
using C::f;
f<3>(b); //well-formed because C::f is visible; then
// A::f is found by argument dependent lookup
}
Autres conseils
Je voudrais affiner réponse peu acceptée. On ne sait pas dans la question de l'OP, mais la partie importante de la norme (cité par Kornel) est ce (Souligné par l'auteur):
Mais quand un modèle de fonction arguments de modèle explicites est utilisé, l'appel n'a pas la forme syntaxique correcte
ce qui est interdit se fonde sur ADL et en utilisant des arguments de modèle explicites. Malheureusement, en utilisant des arguments de modèle non-type nécessite l'aide d'arguments explicites (à moins qu'ils ont des valeurs par défaut).
Ci-dessous un exemple de code montrant ceci:.
#include <string>
#include <utility>
namespace C {
struct B { };
template<class T> void f(T t){}
}
void g(C::B b) {
f(b); // OK
//f<C::B>(b); // ill-formed: not a function call, but only
// because explicit template argument were used
std::string s;
move(s); // OK
//move<std::string&>(s); // Error, again because
// explicit template argument were used
std::move<std::string&>(s); // Ok
}
int main()
{
C::B b;
g(b);
}
Depuis c ++ 20, adl fonctionne aussi très bien avec le modèle de fonction explicite. Voici la proposition: P0846R0: Modèles ADL et fonctions qui ne sont pas visibles :
Au lieu de demander à l'utilisateur d'utiliser le mot-clé de modèle, une révision des règles de consultation a été proposée afin qu'un nom pour lequel une recherche normale produit soit aucun résultat ou trouve une ou plusieurs fonctions et qui est suivi par aa « < » Would traitée comme si un nom de modèle de fonction avait été trouvé et causerait ADL à réaliser.
À l'heure actuelle, seule GCC 9 a implémenter cette fonctionnalité, de sorte que votre exemple peut compiler.
Edit: Non, ce n'est pas juste. Voir @ Kornel de réponse.
Je ne suis pas tout à fait sûr, mais après avoir consulté "Le langage C ++ programmation" de Stroustrup Je pense que la section Annexe C 13.8.4 peut être la cause.
Depuis frob
est un modèle pourrait éventuellement se spécialiser pour i=0
à un point après que vous l'appelez. Cela signifie que la mise en œuvre se retrouveraient avec deux possibilités de choix qui frob
à l'appel comme il semble qu'il peut choisir au moment de l'instanciation ou à la fin du traitement de l'unité de traduction.
Alors, je pense que le problème est que vous pourriez faire
namespace ns {
struct foo {};
template<int i> void frob(foo const&) {}
}
int main() {
ns::foo f;
frob<0>(f);
return 0;
}
namespace ns {
template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}