C ++ 20: 4 行のコードでリンカーを驚かせる

あなたが最新の C ++ 機能を学習している学生であると想像してください。そして、あなたは概念/制約のトピックに関する問題を与えられましたもちろん、教師は「正しいやり方」を参考にした解決策を持っていますが、それはあなたには明らかではなく、それでも機能しないかなり混乱したコードの山に気付くでしょう。(そして、あなたはますます多くのオーバーロードとテンプレートの特殊化を追加して記述し、コンパイラのますます多くの新しい主張をカバーします)。





ここで、あなたがこの山を見て生徒を助けたいと思った教師であると想像してください。あなたはそのコードを単純化し、単純化し始め、ユニットテストの一部にばかげたコメントを付けて、何らかの形で機能させるようにしました. また、単体テストの順序によって、結果が異なるか、まったく収集されません。どこかに未定義の動作を隠しています。でもどっち?





まず、先生 (つまり私) がコードを次のように最小化しました: 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);
}
      
      



私たちはそれを期待しています





  • f (v) は、「一般ケース void f (T) [T = int] のベクトル」を出力します。





  • f (& v) は、「一般ケース void f (T) [T = int] のベクトルへのポインター」を出力します。





代わりに、私たちは得る





  • : 「一般的な場合のベクトル void f (T) [T = int]」





  • B: 「一般的な場合のポインタ 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