0xDEADC0DE
0xDEADC0DE

Reputation: 333

How to use C++20 concepts to check some constraints in the template itself

Sorry for the ambiguous title, I couldn't find the right words to phrase it. I'm trying to force a class to define some functions/operators to be correct. for example ForwardIterator is required to have some operators otherwise I should get a compiler error. I have asked the question here and I was given a method with inheritance, it works perfectly but C++20 concepts seem to be more intuitive and give better errors. The initial idea is:

template <typename T> concept is_ForwardIterator= requires(T x, T y)
{
    x == y;
};
template <typename T>
requires  is_ForwardIterator<T>
struct ForwardIterator {
};

Then whenever I want to implement a forward iterator I inherit from ForwardIterator:

struct MyCustomIterator
   : public ForwardIterator <MyCustomIterator> 
{
  bool operator ==(const MyCustomIterator& other) const;
};

But the compiler keeps complaining that MyCustomIterator doesn't satisfy the requirement:

error: constraints not satisfied for class template 'ForwardIterator' [with T = MyCustomIterator]
note: because 'MyCustomIterator' does not satisfy 'is_ForwardIterator'
template <typename T> requires is_ForwardIterator<T> struct ForwardIterator {
                               ^
note: because 'x == y' would be invalid: invalid operands to binary expression ('MyCustomIterator' and 'MyCustomIterator')
        x == y;

Upvotes: 1

Views: 775

Answers (3)

Jarod42
Jarod42

Reputation: 217235

In CRTP, derived class in incomplete, so checking its property is not possible.

One way to check interface with traits/concept is simply static_assert after the class:

template <typename T> concept is_ForwardIterator= requires(T x, T y)
{
    x == y;
    // ...
};

struct MyCustomIterator
{
    bool operator ==(const MyCustomIterator& other) const;
    // ...
};
static_assert(is_ForwardIterator<MyCustomIterator>);

Upvotes: 1

ecatmur
ecatmur

Reputation: 157344

The problem is that where the constraint on ForwardIterator is specified, the class T (that is, MyCustomIterator) is incomplete - it exists as a class name only.

You need to delay checking of the constraint until after MyCustomIterator is complete. One way to do this is a constraint on the destructor of ForwardIterator:

template <typename T>
struct ForwardIterator {
    ~ForwardIterator() requires is_ForwardIterator<T> {}
};

The only problem here is that this will not be checked until there is a reason to instantiate the destructor ~ForwardIterator, i.e. if you do not actually instantiate a MyCustomIterator object it will not be checked. But this should not be a problem in practice.

Upvotes: 2

Nicol Bolas
Nicol Bolas

Reputation: 473352

When you try to use the CRTP, during the compilation of the base class template, the derived class is incomplete. As such, asking most questions about its behavior is impossible. This includes most of the reasons to use a concept.

What you're trying to do cannot reasonably be done that way. You may need a traits class or something, depending on what your goal is.

Upvotes: 2

Related Questions