Imaginez que vous êtes un étudiant apprenant les fonctionnalités C++ modernes. Et on vous a posé un problème sur le thème des concepts/contraintes . L'enseignant, bien sûr, a une solution de référence "comment bien faire les choses", mais ce n'est pas évident pour vous, et vous trouverez une montagne de code assez déroutant qui ne fonctionne toujours pas. (Et vous ajoutez et écrivez de plus en plus de surcharges et de spécialisations de modèles, couvrant de plus en plus de nouvelles revendications du compilateur).
Imaginez maintenant que vous êtes un enseignant qui a vu cette montagne et qui a voulu aider l'élève. Vous avez commencé à simplifier et simplifier son code, et même à commenter bêtement des morceaux de tests unitaires pour le faire fonctionner d'une manière ou d'une autre... Mais ça ne marche toujours pas. De plus, selon l'ordre des tests unitaires, il produit des résultats différents ou n'est pas collecté du tout. Quelque part caché comportement indéfini. Mais lequel?
Tout d'abord, l'enseignant (c'est-à-dire moi-même) a réduit le code à quelque chose comme ceci : https://gcc.godbolt.org/z/TaMTWqc1T
//
template<class T> concept Ptr = requires(T t) { *t; };
template<class T> concept Vec = requires(T t) { t.begin(); t[0]; };
// ,
template<class T> void f(T t) { // (1)
std::cout << "general case " << __PRETTY_FUNCTION__ << std::endl;
}
template<Ptr T> void f(T t) { // (2)
std::cout << "pointer to ";
f(*t); // ,
}
template<Vec T> void f(T t) { // (3)
std::cout << "vector of ";
f(t[0]); // ,
}
// ( )
int main() {
std::vector<int> v = {1};
//
f(v);
//
f(&v);
//
f(&v);
f(v);
//
f(v);
f(&v);
}
On s'attend à ce que
f (v) affichera "vecteur de cas général vide f (T) [T = int]"
f (& v) affichera "pointeur vers le vecteur du cas général void f (T) [T = int]"
Au lieu de cela, nous obtenons
: "vecteur de cas général vide f (T) [T = int]"
B : "pointeur du cas général void f (T) [T = std :: vecteur <int>]" -?
: clang
"pointer to general case void foo(T) [T = std::vector<int>]" —
"general case void foo(T) [T = std::vector<int>]", — , !
gcc —
: clang gcc
?!
. — , (2) (1) (2), (1).
: https://gcc.godbolt.org/z/47qhYv6q4
void f(int x) { std::cout << "int" << std::endl; }
void g(char* p) { std::cout << "char* -> "; f(*p); } // f(int)
void f(char x) { std::cout << "char" << std::endl; }
void g(char** p) { std::cout << "char** -> "; f(**p); } // f(char)
int main() {
char x;
char* p = &x;
f(x); // char
g(p); // char* -> int
g(&p); // char** -> char
}
- - , , — , .
- , , - , () .
, . . , ODR?
:
template<class T> void f(T t) {.....}
template<class T> void f(T t) requires Ptr<T> {.....}
template<class T> void f(T t) requires Vec<T> {.....}
. . , .
SFINAE, https://gcc.godbolt.org/z/4sar6W6Kq
// char int -
template<class T, class = void> void f(T t, char) {.....}
template<class T> auto f(T t, int) -> std::enable_if_t<Ptr<T>, void> {.....}
template<class T> auto f(T t, int) -> std::enable_if_t<Vec<T>, void> {.....}
..... f(v, 0) .....
..... f(&v, 0) .....
, https://gcc.godbolt.org/z/PsdhsG6Wr
template<class T> void f(T t) {.....}
template<class T> void f(T* t) {.....}
template<class T> void f(std::vector<T> t) {.....}
. , ( - - ), ( f(T*) "general case", main - "vector").
/?
, RSDN, !
4 :
template<class T> void f() {}
void g() { f<int>(); }
template<class T> void f() requires true {}
void h() { f<int>(); }
, . g() , h() - .
! .
, (clang ≤ 12.0, gcc ≤ 12.0) requires . - MSVC6 , ...
, , , . : " -, ill-formed, " (, ill-formed " ", " "...)
2017 , .
. - . , , - . ( , — , ).