C ++ 20: surprise the linker with four lines of code

Imagine that you are a student learning modern C ++ features. And you were given a problem on the topic of concepts / constraints . The teacher, of course, has a reference solution "how to do it right", but it is not obvious to you, and you will find a mountain of rather confusing code that still does not work. (And you add and write more and more overloads and specializations of templates, covering more and more new claims of the compiler).





Now imagine that you are a teacher who saw this mountain and wanted to help the student. You began to simplify and simplify its code, and even stupidly comment on pieces of unit tests to make it work somehow ... But it still doesn't work. Moreover, depending on the order of the unit tests, it produces different results or is not collected at all. Somewhere hidden undefined behavior. But which one?





First, the teacher (that is, I) minimized the code to something like this: 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);
}
      
      



We expect that





  • f (v) will print "vector of general case void f (T) [T = int]"





  • f (& v) will print "pointer to vector of general case void f (T) [T = int]"





And instead we get





  • : "vector of general case void f (T) [T = int]"





  • B: "pointer of general case 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