Я стремлюсь реализовать шаблон структуры, который можно использовать для определения того, правильно ли сформирована замена шаблона или нет. Примером использования является предоставление двух версий функций шаблона в зависимости от того, является ли параметр шаблона сопоставимым или нет.
Это может быть решено довольно легко, если явно указать структуры для каждого сценария, например. существует ли оператор равенства для типа шаблона, как показано здесь. Но мне не удалось реализовать структуру, которая принимала бы (почти) произвольную конструкцию в качестве аргумента шаблона.
«Лучший» подход, которого я достиг до сих пор, использует аргумент шаблона шаблона. Он компилируется, но не подходит для случая, когда подстановка аргумента должна быть правильно сформирована.
#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 никогда не выполняется.
Я несколько близок к правильному решению, или это будет сложнее?
Это действительно решило мою проблему. Может кто-нибудь объяснить мне, почему решение с Enable = T не работает, как я ожидал?





Третий параметр шаблона 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;
using Enable = void;, то естьvoidнеT