Imagine que es un estudiante que aprende las funciones modernas de C ++. Y se le presentó un problema sobre el tema de conceptos / restricciones . El profesor, por supuesto, tiene una solución de referencia "cómo hacerlo bien", pero no es obvio para ti, y encontrarás una montaña de código bastante confuso que todavía no funciona. (Y agrega y escribe más y más sobrecargas y especializaciones de plantillas, cubriendo cada vez más reclamos nuevos del compilador).
Ahora imagina que eres un maestro que vio esta montaña y quiso ayudar al alumno. Comenzó a simplificar y simplificar su código, e incluso a comentar estúpidamente piezas de pruebas unitarias para que funcionara de alguna manera ... Pero todavía no funciona. Además, dependiendo del orden de las pruebas unitarias, produce resultados diferentes o no se recopila en absoluto. En algún lugar oculto comportamiento indefinido. ¿Pero cual?
Primero, el maestro (es decir, yo) minimizó el código a algo como esto: 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);
}
Esperamos que
f (v) imprimirá "vector de caso general vacío f (T) [T = int]"
f (& v) imprimirá "puntero al vector del caso general vacío f (T) [T = int]"
En cambio, obtenemos
: "vector de caso general vacío f (T) [T = int]"
B: "puntero de caso general void f (T) [T = std :: vector <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 , .
. - . , , - . ( , — , ).