У меня есть следующее:
struct Base {};
struct Derived : public Base {};
struct DualHandler {
static void handle(Base&) {}
static void handle(Derived&) {}
};
struct SingleHandler {
static void handle(Base&) {}
};
Я хочу определить концепцию, которая удовлетворяется DualHandler
, но не SingleHandler
. Как я могу это сделать?
Моя наивная попытка:
template<typename T>
concept IsDualHandler = requires(Base& b, Derived& d) {
{ T::handle(b) };
{ T::handle(d) };
};
Но, к сожалению, SingleHandler
удовлетворяет IsDualHandler
, потому что Derived&
можно передать любой функции, которая ожидает Base&
.
Да, я понимаю Derived
"есть" Base
. Я хочу во время компиляции определить, имеет ли данный класс обработчика специализацию для определенного типа Derived
.
@dshin: концепции не предназначены для определения точных типов; они предназначены для обнаружения выражений. Вот почему они определяются допустимостью выражений, а не серией объявлений, которые должны существовать. Логика вызова функций C++ требует, чтобы, если тип публично унаследован от другого, если функция принимает ссылку на базовый класс, она должна принимать ссылку на экземпляр производного класса точно таким же образом.
@NicolBolas Я понимаю, что концепции созданы для требований к выражениям, а не к типам. Но в определенных контекстах я хочу использовать их для того, для чего они не предназначены (требования к типу). Конечно, требования к типам — это разумный недостаток концепций; иначе почему такие языки, как Rust, поддерживают их как концептуальный эквивалент? Пока C++ не предоставит более удобный механизм для указания требований к типу параметров типа шаблона, мы вынуждены искать творческие обходные пути, подобные тем, которые представлены в ответах на этот вопрос.
template<class T>
struct be_exactly {
template<class U> requires (std::same_as<std::decay_t<T>,std::decay_t<U>>)
operator U&();
};
это пытается точно сопоставить аргумент функции.
template<typename T>
concept IsDualHandler = requires(be_exactly<Base> b, be_exactly<Derived> d) {
{ T::handle(b) };
{ T::handle(d) };
};
это их отличает.
Живой пример.
Мне не нравится, когда ссылки жестко запрограммированы; вы можете исправить это, используя слишком много шаблонов.
Обратите внимание, что класс с template <typename T> static void handle(T&) {}
не будет проверять IsDualHandler
.
Мне кажется, что правильно, что
SingleHandler
удовлетворяетIsDualHandler
, поскольку он действительно может правильно обрабатыватьDerived
, посколькуDerived
"является"Base
в силу определения вашего класса. Какой у вас вариант использования, где это было бы неправильно?