Концепция проверки отсутствия участника

Мне нужен concept, который идентифицирует, когда была выполнена специализация шаблона. Другими словами, по умолчанию он возвращает false и возвращает true только в том случае, если была выполнена настройка. Я успешно заставил это работать со следующим кодом.

Обновлено: Ссылка на рабочий код: https://godbolt.org/z/Pbnqz1Mrd

template <typename T>
struct WrapperEnabled
{
    static constexpr const bool disabled = true;
};

template <typename T>
concept WrapperDisabled = WrapperEnabled<T>::disabled;

template <typename T>
concept Wrappable = !WrapperDisabled<T>;

Wrappable<T> теперь будет ложным для каждого типа данных, если вы не сделали это в своем коде:

template <>
struct WrapperEnabled<MyClass>;

Таким образом, я могу затем использовать его в объявлениях шаблонов:

template<Wrappable T>
class X { ... }

Отлично, пока все хорошо. Мой вопрос заключается в том, могу ли я определить концепцию Wrappable напрямую, вместо того, чтобы нуждаться в промежуточной концепции WrapperDisabled. Согласно моим экспериментам, любая логическая логика, выполняемая с несуществующим членом, просто приводит к тому, что все выражение преобразуется в false . Что-то вроде этого, например:

template <typename T>
concept Wrappable = (!WrapperEnabled<T>::disabled); // Error!

Есть ли способ сделать это?

Не уверен, что я что-то упускаю или нет, но вы нигде не используете WrapperEnabled, поэтому я не понимаю, как добавление специализации для него заставляет ваш код работать.

NathanOliver 07.08.2024 20:44

Не проще ли было бы проверить свойства, которыми должен обладать класс Wrappable, чем просить программиста явно добавить template <> struct WrapperEnabled<MyClass>; после того, как класс будет готов? Это не очень удобно для пользователя.

Ted Lyngmo 07.08.2024 21:04

@NathanOliver Ошибка копирования и вставки. Я оставил некоторые старые имена классов. Исправлено сейчас. Вы можете видеть, что используются два размещенных WrapperEnabled. По сути, специализируя WrapperEnabled, несмотря на то, что теоретически ничего не делается, вы «удаляете» отключенный статический элемент. Таким образом, эффективно добавляем его в статический набор классов во время компиляции.

Francisco Ryan Tolmasky I 07.08.2024 21:14

@FranciscoRyanTolmaskyI Но для чего это будет использоваться? Если класс уже соответствует всем требованиям, почему автор класса должен явно сообщать об этом компилятору через отдельное объявление нереализованного класса?

Ted Lyngmo 07.08.2024 21:17

@FranciscoRyanTolmaskyI Я согласен с Тедом. Концепты предназначены для проверки наличия функциональности. Если класс не реализует требуемую функциональность, концепция просто не будет соответствовать классу. Что значит, чтобы класс был Wrappable? Что именно заворачивается?

Remy Lebeau 07.08.2024 21:17

@TedLyngmo Мне приходится использовать этот обходной метод, потому что мне приходится применять его к существующим классам, которые я не контролирую. Рассматриваемый фреймворк использует перегруженные функции для выполнения множества внутренних операций (что-то вроде createWrapper<T>() { ... }). Я хочу добавить возможность переноса к большему количеству существующих классов библиотеки, но это будет означать ручную перегрузку каждого класса, несмотря на то, что все они имеют одинаковую реализацию. Давайте я просто сделаю одну перегрузку: template<Wrappable T> createWrapper() { ...custom_code... }. А затем отмечаю те, которые хочу посчитать.

Francisco Ryan Tolmasky I 07.08.2024 21:19

@FranciscoRyanTolmaskyI Я имею в виду, что если вы сделаете concept, чтобы он проверял все, что составляет класс Wrappable, вам не нужно добавлять еще одну строку для реальных классов. Что, если вы ошибетесь и создадите класс Wrappable, в котором есть какой-то недостаток? Не лучше ли было бы проверить, что именно делает класс Wrappable?

Ted Lyngmo 07.08.2024 21:21

@FranciscoRyanTolmaskyI, было бы полезно, если бы вы предоставили более полный пример того, что именно вы пытаетесь сделать. Это начинает звучать как пограничная Проблема XY. Вам следует написать concept, чтобы найти то, что вы хотите добавить поверх существующих классов. Если эти проблемы невозможно решить, concept не будет совпадать.

Remy Lebeau 07.08.2024 21:23

@TedLyngmo Вот ссылка на рабочий код: godbolt.org/z/Pbnqz1Mrd . Я ценю желание сделать что-то совершенно другое, но не думаю, что это здесь уместно. У меня есть вещь, которая работает. Я знаю, что можно просто изменить иерархию классов для другого решения. Большой. Подумайте об этом, мне просто интересно узнать об этой логической головоломке, если хотите. Я предоставил рабочий код и просто хочу знать, способен ли язык C++ достичь того же конечного результата без промежуточной «вспомогательной концепции».

Francisco Ryan Tolmasky I 07.08.2024 21:37

@RemyLebeau Я думаю, что приведенный выше комментарий также отвечает на ваш вопрос, но StackOverflow не позволяет вам отмечать двух человек.

Francisco Ryan Tolmasky I 07.08.2024 21:38

у вас есть концепция, которую вы хотите, так что в ней плохого? Концепции предназначены для проверки выполнения некоторых требований. Если вы хотите проверить, что какое-то требование не выполнено, вполне естественно, что вы определяете концепцию и отрицаете ее.

463035818_is_not_an_ai 07.08.2024 21:41

вы можете позволить пользователю определить какой-либо член wrappable и проверить его наличие. Но обсуждение показывает, что вы не хотите этого, а хотите именно ту концепцию, которая у вас уже есть;)

463035818_is_not_an_ai 07.08.2024 21:43

@ 463035818_is_not_an_ai В этом нет ничего плохого, я просто хотел узнать, есть ли способ сделать это без промежуточного шага. Вот и весь вопрос. Если ответ отрицательный, то хорошо. Я просто хотел знать, действительно ли ответ «нет» или нет. Я знаю, что нынешняя вещь работает, это был вопрос о том, чтобы узнать о возможностях этой языковой функции, или, возможно, существовал какой-то std::enable_if_missing или что-то в этом роде.

Francisco Ryan Tolmasky I 07.08.2024 21:46

связанное stackoverflow.com/questions/63435642/negating-a-concept-c20

463035818_is_not_an_ai 07.08.2024 21:48

и есть en.cppreference.com/w/cpp/types/negation

463035818_is_not_an_ai 07.08.2024 21:48

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

463035818_is_not_an_ai 07.08.2024 21:50

@FranciscoRyanTolmaskyI, этот пример с божьим болтом по сути такой же, как и в вашем вопросе. Вы просите чего-то более простого, но, не видя, как вы на самом деле собираетесь использовать эти переносимые/модифицированные типы после того, как вы их обнаружили, трудно предоставить вам более простые альтернативы. Отсюда необходимость увидеть минимально воспроизводимый пример , который вы хотите упростить.

Remy Lebeau 07.08.2024 21:50

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

Ted Lyngmo 07.08.2024 22:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
18
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Возможно, вы ищете (отрицаемое) вложенное требование вроде этого:

template<typename T>
concept Wrappable = !requires { requires WrapperEnabled<T>::disabled; };

Это true тогда и только тогда, когда либо

  • WrapperEnabled<T>::disabled имеет правильный формат и оценивается, явно преобразуясь в bool, в false или if
  • WrapperEnabled<T>::disabled неправильно сформирован.

Это все равно не удастся, если WrapperEnabled<T>::disabled правильно сформировано, но не является постоянным выражением.

Выражение requires с вложенным требованием requires { requires X; }, приведенное выше, ведет себя как проверка C<T> на удовлетворение, где C определяется как template<typename T> concept C = X;.

Вы можете упростить это, опустив промежуточное WrapperDisabledconcept:

template <class T>
struct WrapperEnabled {
    using enabled = void;
};

template <class T>
concept Wrappable = requires { typename WrapperEnabled<T>::enabled; };

Для этого необходимо, чтобы typename WrapperEnabled<T>::enabled был правильно сформирован. Неважно, к какому типу вы определили enabled.

Демо

Другие вопросы по теме