Универсальная структура `Is_enabled` SFINAE - PullRequest
0 голосов
/ 08 июня 2019

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

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

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

#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 никогда не заканчивается.

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

1 Ответ

2 голосов
/ 08 июня 2019

Третий параметр шаблона 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.

...