1

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;
0xDEADC0DE
  • 323
  • 3
  • 12
  • 1
    What is the purpose of `ForwardIterator`? Just a way to tag and check the custom type for satisfaction of constraints? And if so, if the type has to opt in to this check, why can't one just static assert on the type after the full class definition? `class MyCustomIterator{}; static_assert(is_ForwardIterator);` – StoryTeller - Unslander Monica Dec 28 '20 at 17:17
  • 1
    Are you aware that the parameter to the `==` overload should be a `const` reference parameter, and not a mutable reference parameter? – Sam Varshavchik Dec 28 '20 at 17:19
  • 1
    It's a bit odd to add constraints on a class in it's definition. Usually you would want to check the constraints when the class is used. – super Dec 28 '20 at 17:58

3 Answers3

2

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
1

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>);
Jarod42
  • 203,559
  • 14
  • 181
  • 302