CPP Playground

Curious Case of Concepts (C++ 20) CLANG++(v12) vs G++(v10)

First, you want to write a simple operator<< overload to print your std::vectors

First iteration:

  1.      template <typename T>
  2.     ostream& operator<< (ostream& os, const vector<T>& v) {
  3.         for (auto& e: v) {
  4.             os << e << " ";
  5.         }
  6.         return os;
  7.     }

Now, let us look at a sample main() which uses this

  1.   int main(){
  2.       vector<int> vint(10, 3);  
  3.       cout << vint << endl;     // works!
  4.  
  5.       vector<vector<int>> vvint(5, vector<int>(2, 9)); 
  6.       cout << vvint << endl;    // works!
  7.  
  8.       vector <pair<int,int>> vpair(2);
  9.       cout << vpair << endl;   // doesn't; obviously! 
  10.   }

What if we want to change our operator overload to be only used if the passed type is indeed that can be printed? E.g., line 9 of the main should have some readable error message in it.

Second Iteration:

  1. template <typename T>
  2. concept printable =  requires (T t) {
  3.     cout << t;
  4. };

  5. template <printable T>
  6. ostream& operator<< (ostream& os, const vector<T>& v) {
  7.     for (auto& e: v) {
  8.         os << e << " ";
  9.     }
  10.     return os;
  11. }

with this update to our code, now the failure on main()'s line 9 has a better error message:

g++10:
concept.cpp:27:14: note: constraints not satisfied

concept.cpp: In instantiation of ‘std::ostream& operator<<(std::ostream&, const std::vector<T>&) [with T = std::pair<int, int>; std::ostream = std::basic_ostream<char>]’:concept.cpp:44:10:   required from hereconcept.cpp:8:9:   required for the satisfaction of ‘printable<T>’ [with T = std::pair<int, int>]concept.cpp:8:22:   in requirements with ‘T t’ [with T = std::pair<int, int>]concept.cpp:9:7: note: the required expression ‘(std::cout << t)’ is invalid

clang++12:

concept.cpp:27:14: note: candidate template ignored: constraints not satisfied [with T = std::pair<int, int>]    ostream& operator<< (ostream& os, const vector<T>& v) {             ^concept.cpp:26:12: note: because 'std::pair<int, int>' does not satisfy 'printable'        template <printable T>                  ^concept.cpp:9:7: note: because 'cout << t' would be invalid: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'std::pair<int, int>')        cout << t;

However, this is where it gets interesting!

clang++12 fails for 


cout << vvint << endl;

as well; with a similar error.

concept.cpp:27:14: note: candidate template ignored: constraints not satisfied [with T = std::vector<int>]    ostream& operator<< (ostream& os, const vector<T>& v) {             ^concept.cpp:26:12: note: because 'std::vector<int>' does not satisfy 'printable'        template <printable T>                  ^concept.cpp:9:7: note: because 'cout << t' would be invalid: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup        cout << t;

Need to inspect how g++ and Clang++ actually implement concepts behding the scene - looks like g++ uses a recursive-kinda check for the satisfiability.


to be continued...