Проверка концептов внутри типа
От: K13 http://akvis.com
Дата: 10.12.24 09:16
Оценка:
#include <concepts>

template< typename T >
concept cloneable = requires( T x )
{
    { x.clone() } -> std::convertible_to<T*>;
};

template< cloneable T >
struct OnlyCloneable {};

struct Foo
{
    Foo* clone() const { return new Foo(*this); }
    //using Ptr1 = OnlyCloneable<Foo>; // если раскомментарить, то ошибка компиляции
};

using Ptr2 = OnlyCloneable<Foo>;

static_assert( cloneable<Foo> );

int main()
{
    return 0;
}

Вопрос -- это как-то лечится?
Почему проваливается проверка концепта внутри типа, хотя наличие метода clone компилятору уже известно?
Re: Проверка концептов внутри типа
От: rg45 СССР  
Дата: 10.12.24 09:47
Оценка: +1
Здравствуйте, K13, Вы писали:

K13>
K13>#include <concepts>

K13>template< typename T >
K13>concept cloneable = requires( T x )
K13>{
K13>    { x.clone() } -> std::convertible_to<T*>;
K13>};

K13>template< cloneable T >
K13>struct OnlyCloneable {};

K13>struct Foo
K13>{
K13>    Foo* clone() const { return new Foo(*this); }
K13>    //using Ptr1 = OnlyCloneable<Foo>; // если раскомментарить, то ошибка компиляции
K13>};

K13>using Ptr2 = OnlyCloneable<Foo>;

K13>static_assert( cloneable<Foo> );

K13>int main()
K13>{
K13>    return 0;
K13>}
K13>

K13>Вопрос -- это как-то лечится?
K13>Почему проваливается проверка концепта внутри типа, хотя наличие метода clone компилятору уже известно?

Почему, понятно — потому что в этой точке Foo ещё не является полным типом. Примерно то же получится, если в этой точке попытаться полчучить sizeof(Foo). Выход сходу вижу только один — заводить Ptr1 внутрь функции-члена.

P.S. Возможно также, что это повод для того, чтобы пересмотреть дизайн на предмет избыточных и циклических зависимостей между типами.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 10.12.2024 10:26 rg45 . Предыдущая версия . Еще …
Отредактировано 10.12.2024 9:49 rg45 . Предыдущая версия .
Re: Проверка концептов внутри типа
От: so5team https://stiffstream.com
Дата: 10.12.24 09:48
Оценка:
Здравствуйте, K13, Вы писали:

K13>Почему проваливается проверка концепта внутри типа, хотя наличие метода clone компилятору уже известно?


Судя по выхлопу clang-а компилятор считает Foo все еще незавершенным типом. И, в принципе, его можно понять, вдруг ниже будет еще одна перегрузка для clone.
Re: Проверка концептов внутри типа
От: vopl Россия  
Дата: 10.12.24 10:44
Оценка:
Здравствуйте, K13, Вы писали:

K13>Вопрос -- это как-то лечится?

K13>Почему проваливается проверка концепта внутри типа, хотя наличие метода clone компилятору уже известно?

Почему — уже пояснили выше. Полечить таки можно, если отложить инстанцирование концепта на потом. Например так
https://gcc.godbolt.org/z/b9z5qs7rK
Re[2]: Проверка концептов внутри типа
От: rg45 СССР  
Дата: 10.12.24 10:54
Оценка:
Здравствуйте, vopl, Вы писали:

V>Почему — уже пояснили выше. Полечить таки можно, если отложить инстанцирование концепта на потом. Например так

V>https://gcc.godbolt.org/z/b9z5qs7rK

Так для использования снаружи нет и смысла заводить Ptr1 внутри Foo. Как я понял, в том-то и состоит подвох, что, вероятно, он хочет использовать Ptr1 внутри Foo, и не только внутри функций-членов.
--
Справедливость выше закона. А человечность выше справедливости.
Re: Проверка концептов внутри типа
От: sergii.p  
Дата: 10.12.24 11:51
Оценка:
Здравствуйте, K13, Вы писали:

K13>Вопрос -- это как-то лечится?


можно через CRTP

#include <concepts>

template<typename T>
struct Clonable {
    T clone() { return new T(static_cast<T*>(*this)); } // up-cast here
};

template<typename T>
concept cloneable = requires (T x) {
    static_cast<Clonable<T>>(x);
};

template< cloneable T >
struct OnlyCloneable {};

struct Foo: Clonable<Foo>
{
    using Ptr1 = OnlyCloneable<Foo>;
};

struct Bar {};

static_assert( cloneable<Foo> );
//static_assert( cloneable<Bar> );

int main()
{
    return 0;
}
Отредактировано 10.12.2024 11:57 sergii.p . Предыдущая версия .
Re[2]: Проверка концептов внутри типа
От: rg45 СССР  
Дата: 10.12.24 12:15
Оценка: +1
Здравствуйте, sergii.p, Вы писали:

SP>
SP>template<typename T>
SP>concept cloneable = requires (T x) {
SP>    static_cast<Clonable<T>>(x);
SP>};
SP>


Тогда уж проще:

template<typename T>
concept cloneable = std::derived_from<T, Cloneable<T>>;


Только закладывать в концепт наследование от конкретного класса — идея так себе, по-моему. Если на каждый концепт заводить по базовому классу — Cloneable, Printable, Loggagle, Serializable, Drawable, Sendable, Receivable, Insertable, Extractable... Программа превращается в зоопарк монстров. Концепты же для того и придумывались, чтоб этого всего не было. Чтоб можно было реализовывать обобщенные алгоритмы, применимые как пользовательским класам, так и к встроенными и библиотечным типам.

Я бы, все-таки, пошерстил дизайн на предмет циклических зависимостей.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 10.12.2024 13:08 rg45 . Предыдущая версия . Еще …
Отредактировано 10.12.2024 13:07 rg45 . Предыдущая версия .
Отредактировано 10.12.2024 12:51 rg45 . Предыдущая версия .
Отредактировано 10.12.2024 12:46 rg45 . Предыдущая версия .
Отредактировано 10.12.2024 12:45 rg45 . Предыдущая версия .
Re[3]: Проверка концептов внутри типа
От: sergii.p  
Дата: 10.12.24 13:09
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, sergii.p, Вы писали:


R>Тогда уж проще:


R>
R>template<typename T>
R>concept cloneable = std::derived_from<T, Cloneable<T>>;
R>


лично у меня не скомпилировалось. Похоже derived_from требует полного типа.

R>Только закладывать в концепт наследование от конкретного класса — идея так себе, по-моему


я всегда их понимал как одно и тоже. В Rust трейты — фактически реализация наследования. Но в тоже время — это не что иное как концепты. В общем не вижу ничего плохого в смешении.
Re[4]: Проверка концептов внутри типа
От: rg45 СССР  
Дата: 10.12.24 13:18
Оценка:
Здравствуйте, sergii.p, Вы писали:

R>>
R>>template<typename T>
R>>concept cloneable = std::derived_from<T, Cloneable<T>>;
R>>


SP>лично у меня не скомпилировалось. Похоже derived_from требует полного типа.


А, ну да. Ну это и не принципиально. По семантике это, так или иначе, требование наследования.

SP>я всегда их понимал как одно и тоже. В Rust трейты — фактически реализация наследования. Но в тоже время — это не что иное как концепты. В общем не вижу ничего плохого в смешении.


В смешении в смысле совместного использования ничего страшного и нет. А вот когда в концепт заложено требование наследования — это сразу же наламывает принцип обобщённости. Взять тот же cloneable — почему бы мне не сделать клонируемыми, например, классы стандартных контейнеров, у которых типы элементов отвечают требованиям этого концепта? Ну да, для этого в концепте не следует закладываться и на функции-члены. Но это уже зло меньшего порядка, чем наследование, это легче исправить.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 10.12.2024 13:19 rg45 . Предыдущая версия .
Re[3]: Проверка концептов внутри типа
От: K13 http://akvis.com
Дата: 10.12.24 18:10
Оценка:
Здравствуйте, rg45, Вы писали:

R>Я бы, все-таки, пошерстил дизайн на предмет циклических зависимостей.


Большая часть кода написана до c++11, и внутри многих классов определен тип Ptr,
который может быть и с подсчетом ссылок, и уникальным (тогда unique_ptr еще не было)
и Copy-on-Write если число владельцев (не читателей/писателей) больше 1...
Зато универсальный метод хранения любой сущности Foo -- в обертке Foo::Ptr,
где Ptr -- специализация одного из шаблонов типом Foo.

Варианты уникального и copy-on-write требуют наличия либо метода clone(), либо copy-ctor.
Первый -- для копирования, второй -- для доступа на запись, делать копию под эксклюзивное владение.

Придется оставить как было -- static_assert в теле метода.
Re[4]: Проверка концептов внутри типа
От: vdimas Россия  
Дата: 11.12.24 00:51
Оценка: +1 -1
Здравствуйте, K13, Вы писали:

K13>Варианты уникального и copy-on-write требуют наличия либо метода clone(), либо copy-ctor.


Тогда просто не используй концепт.
При отсутствии нужного clone() и так будет ошибка компиляции.


K13>Придется оставить как было -- static_assert в теле метода.


А если в коде clone по-факту не используется, то нафига?

=========
Меня, кстате, концепты этим немного и смущают, что они могут "требовать лишнего".

Они хороши для выражения ограничений, но менее хороши для утиной типизации, которой никакая помощь концептов не требуется — она и так отродясь в шаблонах была.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.