C ++ 20: sorprende al enlazador con cuatro líneas de código

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 , .





. - . , , - . ( , — , ).








All Articles