C++ concepts

Sep 11, 2025

I implemented a templated class in C++. One of its methods had an equality check, which made me wonder how C++ handled the fact that my generic type T might not support the == operator.

Let’s consider the following class to illustrate:

template <typename T> class A {

public:
  A(T v) { value = v; }

  bool is(T v) { return value == v; }

private:
  T value;
};

You see the equality check value == v there. But v can be of any type. How does C++ handle that? Is there a way of enforcing that T must support ==?

So let’s consider the following struct Foo:

struct Foo {
  std::string s;
};

So, the program below compiles just fine.

int main()
{
    A<Foo> foo1 = Foo{"Hello"};
    return 0;
}

Which is weird because we know that if we call foo1.is the program will likely break.

Let’s call foo1.is, then:

int main()
{
    A<Foo> foo1 = Foo{"Hello"};
    std::cout << foo1.is(Foo{"Hello"}) << "\n";
    return 0;
}

Now the program fails to compile:

main.cpp: In instantiation of ‘bool A<T>::is(T) [with T = Foo]’:
main.cpp:35:25:   required from here
main.cpp:20:20: error: no match for ‘operator==(operand types are ‘Foo’ and ‘Foo’)
   20 |       return value == v;
      |              ~~~~~~^~~~

So, it is only when we call the function that executes the unsupported operation that the compiler complains. I wondered if there was a way to enforce this requirement at the time of object instantiation, and there is. C++20 introduced concepts, a way of specifying requirements explicitly. We can add the concept std::equality_comparable to our template:

#include <concepts>

template <std::equality_comparable T> 
class A {
  
  public: 
  A(T v) {
   value = v;   
  }
  
  bool is(T v) {
      return value == v;
  }
  
  private:
    T value;
};

and we get a compilation error at the instantiation:

main.cpp: In function ‘int main()’:
main.cpp:35:10: error: template constraint failure for ‘template  requires  equality_comparable class A’
   35 |     A<Foo> foo1 = Foo{"Hello"};
      |          ^
main.cpp:35:10: note: constraints not satisfied

If we now add support for the == operator in Foo, the program compiles:

struct Foo {
  std::string s;
  
  bool operator==(const Foo& other) const {
        return s == other.s;
  }
};

#c++ #concepts