Gaming Life

一日24時間、ゲームは10時間

【C++20】constメンバ関数の実装を要求するconcept

C++20から導入されるコンセプトは、requires節を使用して、型に対してあるメンバ関数の実装を要求することが出来る。 そのメンバ関数に対して、const関数であることを要求できないかと考え、実装してみた。

#include <type_traits>

template <typename T>
concept Drawable = requires(T x) {
    // 型がdraw() const:関数を持つことを要求する
    std::declval<std::add_const_t<T>>().draw();
};

template <Drawable T>
void f(T& x) {
    x.draw();
}

struct A {
    void draw() const {}
};
struct B {
    void draw() {}
};
struct C {
    void func() {}
};

int main() {
    A a;
    B b;
    C c;
    f(a);
    f(b); // draw()関数が非constなのでコンパイルエラー
    f(c); // draw()関数をもたないのでコンパイルエラー
}

wandbox.org

※追記

他の書き方もあるみたい。

// conceptの引数型をconst型にする
template <typename T>
concept Drawable = requires(const T& x) {
    { x.draw() };
};

// 非constメンバ関数の実装も同時に制約条件に加えたい場合
// 1.
template <typename T>
concept ActorClass = requires(const T& x, T& y) {
    { x.draw() };
    { y.update() };
};

// 2.
template <typename T>
concept ActorClass = requires(const T& x) {
    { x.draw() };
} and
requires(T& x) {
   { x.update() };
};

// 3.
template <typename T>
concept ActorClass = requires(const T& x) {
    { x.draw() };
    { const_cast<T&>(x).update() };
};

個人的には、2の書き方が記述量こそ多いが一番直感的かなと思う。が、どれも特に挙動に違いはないと思われるので、好きなのを使えばいい。