Jusqu'à la norme C ++ 20 de C ++, lorsque nous voulions définir un opérateur hors classe qui utilise certains membres privés d'une classe de modèle, nous utilisions une construction similaire à ceci:
template <typename T>
class Foo;
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
int main() {
return 1 == Foo<int>(1) ? 0 : 1;
}
Depuis C ++ 20, cependant, nous pouvons omettre la déclaration hors classe, donc aussi la déclaration avant, afin que nous puissions nous contenter de:
template <typename T>
class Foo {
public:
constexpr Foo(T k) : mK(k) {}
constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
T mK;
};
template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
return lhs == rhs.mK;
}
Maintenant, ma question est, quelle partie de C ++ 20 nous permet de le faire? Et pourquoi cela n'était-il pas possible dans les normes C ++ antérieures?
Comme cela a été souligné dans les commentaires, clang n'accepte pas ce code présenté dans la démo, ce qui suggère que cela pourrait en fait être un bogue dans gcc.
J'ai déposé un rapport de bug sur le bugzilla de gcc
"c string" == Foo<std::string>("foo")
)).Réponses:
GCC a un bug.
La recherche de nom est toujours effectuée pour les noms de modèle apparaissant avant a
<
, même lorsque le nom en question est le nom déclaré dans une déclaration (ami, spécialisation explicite ou instanciation explicite).Étant donné que le nom
operator==
dans la déclaration d'ami est un nom non qualifié et est soumis à la recherche de nom dans un modèle, les règles de recherche de nom en deux phases s'appliquent. Dans ce contexte,operator==
n'est pas un nom dépendant (il ne fait pas partie d'un appel de fonction, donc ADL ne s'applique pas), donc le nom est recherché et lié au point où il apparaît (voir [temp.nondep] paragraphe 1). Votre exemple est mal formé car cette recherche de nom ne trouve aucune déclaration deoperator==
.Je m'attendrais à ce que GCC accepte cela en mode C ++ 20 en raison de P0846R0 , qui permet (par exemple)
operator==<T>(a, b)
d'être utilisé dans un modèle même si aucune déclaration préalable deoperator==
modèle n'est visible.Voici un testcase encore plus intéressant:
Avec
-DWRONG_DECL
, GCC et Clang conviennent que ce programme est mal formé: la recherche non qualifiée de la déclaration d'ami # 2, dans le contexte de la définition du modèle, trouve la déclaration # 1, qui ne correspond pas à l'ami instancié deFoo<int>
. La déclaration n ° 3 n'est même pas prise en compte, car la recherche non qualifiée dans le modèle ne la trouve pas.Avec
-UWRONG_DECL
, GCC (en C ++ 17 et versions antérieures) et Clang conviennent que ce programme est mal formé pour une raison différente: la recherche non qualifiée de laoperator==
ligne # 2 ne trouve rien.Mais avec
-UWRONG_DECL
, GCC en mode C ++ 20 semble décider qu'il est OK que la recherche non qualifiéeoperator==
dans # 2 échoue (probablement en raison de P0846R0), puis semble refaire la recherche à partir du contexte d'instanciation du modèle, trouvant maintenant # 3, dans violation de la règle de recherche de nom en deux phases normale pour les modèles.la source