Общая структура `Is_enabled` SFINAE

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

Это может быть решено довольно легко, если явно указать структуры для каждого сценария, например. существует ли оператор равенства для типа шаблона, как показано здесь. Но мне не удалось реализовать структуру, которая принимала бы (почти) произвольную конструкцию в качестве аргумента шаблона.

«Лучший» подход, которого я достиг до сих пор, использует аргумент шаблона шаблона. Он компилируется, но не подходит для случая, когда подстановка аргумента должна быть правильно сформирована.

#include <iostream>
#include <type_traits>

template <typename T = void, typename...>
using Enable = T;

template <bool Cond, typename T = void>
using Enable_if = typename std::enable_if<Cond, T>::type;

template <typename T, template<typename> class X, typename = void>
struct Is_enabled : std::false_type {};

template <typename T, template<typename> class X>
struct Is_enabled<T, X, Enable<X<T>>> : std::true_type {};

/// An example of construct
template <typename T>
using Equals = decltype(std::declval<T>() == std::declval<T>());

template <typename T>
using Enabled_eq = Enable_if<Is_enabled<T, Equals>::value>;

template <typename T>
using Disabled_eq = Enable_if<!Is_enabled<T, Equals>::value>;

template <typename T>
Enabled_eq<T> foo()
{
    std::cerr << "enabled!" << std::endl;
}

template <typename T>
Disabled_eq<T> foo()
{
    std::cerr << "disabled!" << std::endl;
}

struct A {};

int main(int /*argc*/, const char* /*argv*/[])
{
    foo<int>();  /// should print "enabled!"
    foo<A>();    /// should print "disabled!"
    return 0;
}

В случае int он, очевидно, должен печатать "enabled!", а в случае A он должен печатать "disabled!". Но он всегда печатает "disabled!", поэтому специализация Is_enabled никогда не выполняется.

Я несколько близок к правильному решению, или это будет сложнее?

using Enable = void;, то есть void не T
Piotr Skotnicki 08.06.2019 18:32

Это действительно решило мою проблему. Может кто-нибудь объяснить мне, почему решение с Enable = T не работает, как я ожидал?

Tomáš Kolárik 08.06.2019 18:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Третий параметр шаблона Is_enabled по умолчанию равен void. Это то, что компилятор будет использовать в экземпляре Is_enabled<T, Equals>. То есть Is_enabled<T, X, Enable<X<T>>> : std::true_type {}; можно использовать, только если Enable<X<T>> оценивается как void. Явно передавая аргумент шаблона X<T> в шаблон класса Enable, объявленный как:

template <typename T = void, typename...>
using Enable = T;

вы фактически создаете псевдоним для самого X<T>, а тип void (по умолчанию, необходимый для работы диспетчеризации) вообще не используется. В вашем случае X<T> является результатом спецификатора decltype. Для foo<A>() это приводит к сбою создания экземпляра. Однако для foo<int>() вы получаете тип результата сравнения целых чисел, который является bool. То есть, хотя ошибки подстановки нет, компилятор не может использовать специализацию шаблона класса, потому что он специализирован для void, а не bool.

Чтобы исправить код, вы должны переписать Enable, чтобы он всегда приводил к void:

template <typename...>
using Enable = void;

Это также известно как std::void_t.

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