Определение концепции, требующее ограниченной функции-члена шаблона

Примечание: все последующее использует реализацию Concepts TS в GCC 6.1

Допустим, у меня есть концепция Surface, например:

template <typename T>
concept bool Surface() {
    return requires(T& t, point2f p, float radius) {
        { t.move_to(p) };
        { t.line_to(p) };
        { t.arc(p, radius) };
        // etc...
    };
}

Теперь я хочу определить другую концепцию Drawable, которая соответствует любому типу с функцией-членом:

template <typename S>
    requires Surface<S>()
void draw(S& surface) const;

i.e.

struct triangle {
    void draw(Surface& surface) const;
};

static_assert(Drawable<triangle>(), ""); // Should pass

То есть Drawable - это то, что имеет шаблонную константную функцию-член draw(), принимающую ссылку lvalue на то, что удовлетворяет требованиям Surface. Это довольно легко указать словами, но я не могу понять, как это сделать на C ++ с помощью Concepts TS. "Очевидный" синтаксис не работает:

template <typename T>
concept bool Drawable() {
    return requires(const T& t, Surface& surface) {
        { t.draw(surface) } -> void;
    };
}

ошибка: параметр 'auto' не разрешен в этом контексте

Добавление второго параметра шаблона позволяет компилировать определение концепции, но:

template <typename T, Surface S>
concept bool Drawable() {
    return requires(const T& t, S& s) {
        { t.draw(s) };
    };
}

static_assert(Drawable<triangle>(), "");

Ошибка вывода / замены аргумента шаблона: не удалось вывести параметр шаблона 'S'

теперь мы можем только проверить, соответствует ли конкретная ‹Drawable, Surface> пара концепции Drawable, что не совсем верно. (Тип D либо имеет требуемую функцию-член, либо нет: это не зависит от того, какой именно Surface мы проверяем.)

Я уверен, что можно делать то, что мне нужно, но я не могу разобраться с синтаксисом, и в Интернете пока не так много примеров. Кто-нибудь знает, как написать определение концепции, которое требует, чтобы тип имел ограниченную функцию-член шаблона?


person Tristan Brindle    schedule 06.06.2016    source источник
comment
Вы пробовали использовать std :: is_member_function_pointer ‹Surface› в определении концептов? Может работать. Не могу проверить прямо здесь, так как я не установил gcc на работе ...   -  person gilgamash    schedule 06.06.2016
comment
Возможно то же самое, что и stackoverflow.com/questions/23659382/   -  person ecatmur    schedule 06.06.2016


Ответы (1)


Вы ищете способ, чтобы компилятор синтезировал архетип Surface. То есть некий частный анонимный тип, минимально удовлетворяющий концепции Surface. Как можно меньше. Concepts TS в настоящее время не позволяет использовать механизм автоматического синтеза архетипов, поэтому нам остается делать это вручную. Это довольно сложный процесс , так как очень легко придумать кандидатов в архетипы, которые обладают гораздо большей функциональностью, чем определяет концепция.

В этом случае мы можем придумать что-то вроде:

namespace archetypes {
    // don't use this in real code!
    struct SurfaceModel {
        // none of the special members
        SurfaceModel() = delete;
        SurfaceModel(SurfaceModel const& ) = delete;
        SurfaceModel(SurfaceModel&& ) = delete;
        ~SurfaceModel() = delete;
        void operator=(SurfaceModel const& ) = delete;
        void operator=(SurfaceModel&& ) = delete;

        // here's the actual concept
        void move_to(point2f );
        void line_to(point2f );
        void arc(point2f, float);
        // etc.
    };

    static_assert(Surface<SurfaceModel>());
}

А потом:

template <typename T>
concept bool Drawable() {
    return requires(const T& t, archetypes::SurfaceModel& surface) {
        { t.draw(surface) } -> void;
    };
}

Это действующие концепции, которые, вероятно, работают. Обратите внимание, что архетип SurfaceModel можно еще больше усовершенствовать. У меня есть особая функция void move_to(point2f ), но эта концепция просто требует, чтобы она вызывалась с lvalue типа point2f. Нет требования, чтобы move_to() и line_to() оба принимали аргумент типа point2f, они оба могут принимать совершенно разные вещи:

struct SurfaceModel {    
    // ... 
    struct X { X(point2f ); };
    struct Y { Y(point2f ); };
    void move_to(X );
    void line_to(Y );
    // ...
};

Этот вид паранойи является лучшим архетипом и служит иллюстрацией того, насколько сложной может быть эта проблема.

person Barry    schedule 18.04.2017