Мне нужен 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!
Есть ли способ сделать это?
Не проще ли было бы проверить свойства, которыми должен обладать класс Wrappable
, чем просить программиста явно добавить template <> struct WrapperEnabled<MyClass>;
после того, как класс будет готов? Это не очень удобно для пользователя.
@NathanOliver Ошибка копирования и вставки. Я оставил некоторые старые имена классов. Исправлено сейчас. Вы можете видеть, что используются два размещенных WrapperEnabled. По сути, специализируя WrapperEnabled, несмотря на то, что теоретически ничего не делается, вы «удаляете» отключенный статический элемент. Таким образом, эффективно добавляем его в статический набор классов во время компиляции.
@FranciscoRyanTolmaskyI Но для чего это будет использоваться? Если класс уже соответствует всем требованиям, почему автор класса должен явно сообщать об этом компилятору через отдельное объявление нереализованного класса?
@FranciscoRyanTolmaskyI Я согласен с Тедом. Концепты предназначены для проверки наличия функциональности. Если класс не реализует требуемую функциональность, концепция просто не будет соответствовать классу. Что значит, чтобы класс был Wrappable
? Что именно заворачивается?
@TedLyngmo Мне приходится использовать этот обходной метод, потому что мне приходится применять его к существующим классам, которые я не контролирую. Рассматриваемый фреймворк использует перегруженные функции для выполнения множества внутренних операций (что-то вроде createWrapper<T>() { ... }
). Я хочу добавить возможность переноса к большему количеству существующих классов библиотеки, но это будет означать ручную перегрузку каждого класса, несмотря на то, что все они имеют одинаковую реализацию. Давайте я просто сделаю одну перегрузку: template<Wrappable T> createWrapper() { ...custom_code... }
. А затем отмечаю те, которые хочу посчитать.
@FranciscoRyanTolmaskyI Я имею в виду, что если вы сделаете concept
, чтобы он проверял все, что составляет класс Wrappable
, вам не нужно добавлять еще одну строку для реальных классов. Что, если вы ошибетесь и создадите класс Wrappable
, в котором есть какой-то недостаток? Не лучше ли было бы проверить, что именно делает класс Wrappable
?
@FranciscoRyanTolmaskyI, было бы полезно, если бы вы предоставили более полный пример того, что именно вы пытаетесь сделать. Это начинает звучать как пограничная Проблема XY. Вам следует написать concept
, чтобы найти то, что вы хотите добавить поверх существующих классов. Если эти проблемы невозможно решить, concept
не будет совпадать.
@TedLyngmo Вот ссылка на рабочий код: godbolt.org/z/Pbnqz1Mrd . Я ценю желание сделать что-то совершенно другое, но не думаю, что это здесь уместно. У меня есть вещь, которая работает. Я знаю, что можно просто изменить иерархию классов для другого решения. Большой. Подумайте об этом, мне просто интересно узнать об этой логической головоломке, если хотите. Я предоставил рабочий код и просто хочу знать, способен ли язык C++ достичь того же конечного результата без промежуточной «вспомогательной концепции».
@RemyLebeau Я думаю, что приведенный выше комментарий также отвечает на ваш вопрос, но StackOverflow не позволяет вам отмечать двух человек.
у вас есть концепция, которую вы хотите, так что в ней плохого? Концепции предназначены для проверки выполнения некоторых требований. Если вы хотите проверить, что какое-то требование не выполнено, вполне естественно, что вы определяете концепцию и отрицаете ее.
вы можете позволить пользователю определить какой-либо член wrappable
и проверить его наличие. Но обсуждение показывает, что вы не хотите этого, а хотите именно ту концепцию, которая у вас уже есть;)
@ 463035818_is_not_an_ai В этом нет ничего плохого, я просто хотел узнать, есть ли способ сделать это без промежуточного шага. Вот и весь вопрос. Если ответ отрицательный, то хорошо. Я просто хотел знать, действительно ли ответ «нет» или нет. Я знаю, что нынешняя вещь работает, это был вопрос о том, чтобы узнать о возможностях этой языковой функции, или, возможно, существовал какой-то std::enable_if_missing или что-то в этом роде.
связанное stackoverflow.com/questions/63435642/negating-a-concept-c20
и есть en.cppreference.com/w/cpp/types/negation
честно говоря, когда я открыл ссылку на полный код и увидел, что используются совершенно другие имена, я потерял интерес. Мне потребовалось некоторое время, чтобы понять код в вопросе. Я не хочу тратить больше времени на то, чтобы снова понять один и тот же код. Это просто к вашему сведению. Лучше включите минимально воспроизводимый пример прямо в вопрос, а не представляйте 2 разных кода для одного и того же.
@FranciscoRyanTolmaskyI, этот пример с божьим болтом по сути такой же, как и в вашем вопросе. Вы просите чего-то более простого, но, не видя, как вы на самом деле собираетесь использовать эти переносимые/модифицированные типы после того, как вы их обнаружили, трудно предоставить вам более простые альтернативы. Отсюда необходимость увидеть минимально воспроизводимый пример , который вы хотите упростить.
@FranciscoRyanTolmaskyI Эта ссылка на бога не прояснила, почему вы хотите сделать это таким образом, но хорошо, вы могли бы, по крайней мере, избавиться от отрицающей штуки RetrofittedFeatureDisabled
. Демо
Возможно, вы ищете (отрицаемое) вложенное требование вроде этого:
template<typename T>
concept Wrappable = !requires { requires WrapperEnabled<T>::disabled; };
Это true
тогда и только тогда, когда либо
WrapperEnabled<T>::disabled
имеет правильный формат и оценивается, явно преобразуясь в bool
, в false
или ifWrapperEnabled<T>::disabled
неправильно сформирован.Это все равно не удастся, если WrapperEnabled<T>::disabled
правильно сформировано, но не является постоянным выражением.
Выражение requires
с вложенным требованием requires { requires X; }
, приведенное выше, ведет себя как проверка C<T>
на удовлетворение, где C
определяется как template<typename T> concept C = X;
.
Вы можете упростить это, опустив промежуточное WrapperDisabled
concept
:
template <class T>
struct WrapperEnabled {
using enabled = void;
};
template <class T>
concept Wrappable = requires { typename WrapperEnabled<T>::enabled; };
Для этого необходимо, чтобы typename WrapperEnabled<T>::enabled
был правильно сформирован. Неважно, к какому типу вы определили enabled
.
Не уверен, что я что-то упускаю или нет, но вы нигде не используете
WrapperEnabled
, поэтому я не понимаю, как добавление специализации для него заставляет ваш код работать.