Can you use constraints on derived classes in CRTP methods?
Asked Answered
F

1

8

Is something like this valid C++20 code?

#include <iostream>

template <typename T>
concept impls_decrement = requires(T it) { it.decrement(); };

template <class Derived>
struct iterator_facade {
  Derived& operator--()
    requires impls_decrement<Derived>
  {
    auto& self = static_cast<Derived&>(*this);
    self.decrement();
    return self;
  }
};

struct my_iterator : iterator_facade<my_iterator> {
  void decrement() {
    std::cout << "decrement" << std::endl;
  }
};

int main() {
   my_iterator iter;
   --iter;
   return 0;
}

Adapted from vector-of-bool blog post.

With the latest version of gcc, the code works fine, but the latest version of clang gives this error:

prog.cc:25:4: error: cannot decrement value of type 'my_iterator'
   --iter;
   ^ ~~~~
1 error generated.

Which compiler is correct?

Faizabad answered 16/2, 2021 at 1:8 Comment(2)
gcc is correct. This is basically clang bug 44833. This is how view_interface is specified for Ranges.Telegonus
Derived is incomplete within CRTP definition, but not for CRTP function members instantiation.Grochow
F
3

As Barry mentioned, the code is valid and this is Clang bug 44833.

In the meantime, I was able to work around the issue by writing the code like this (demo):

#include <iostream>


template <typename T>
concept impls_decrement = requires(T it) { it.decrement(); };

template <class Derived>
struct iterator_facade {
  template <class T=Derived>
    requires impls_decrement<T>
  Derived& operator--()
  {
    auto& self = static_cast<Derived&>(*this);
    self.decrement();
    return self;
  }
};

struct my_iterator : iterator_facade<my_iterator> {
  void decrement() {
    std::cout << "decrement" << std::endl;
  }
};

int main() {
   my_iterator iter;
   --iter;
   return 0;
}
Faizabad answered 26/2, 2021 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.