Если я правильно понимаю ваш вопрос, то ваш подход можно заставить работать, например
template <template <class> class Slot, class DefaultType>
struct GetType
{
template <typename T>
static Slot<T>&& deduce(T&&);
static DefaultType&& deduce(...);
template <typename T>
using type = std::remove_reference_t<decltype(deduce(std::declval<T>()))>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<Slot, DefaultType>::template type<T>;
демо здесь
Проблема с вашей первоначальной попыткой заключалась в том, что при вызове функции check
в выражении для decltype()
требовался какой-то аргумент для разрешения перегрузки, чтобы могла произойти магия SFINAE . Мой пример выше опирается на std :: declval , чтобы ввести фиктивный аргумент необходимого типа. Также обратите внимание, что мои вспомогательные функции используют ссылки, а не передают типы по значению напрямую. Это так, что он также работает с типами, которые не могут быть скопированы. Обратите внимание, что будут проблемы, если Slot<T>
или DefaultType
сами являются ссылочными типами. Можно было бы, например, ввести дополнительные типы обертки, чтобы справиться с этим & hellip;
В качестве альтернативы, вы можете использовать частичную специализацию шаблона класса, чтобы выбрать правильный тип, например:
template <class T, template <class> class Slot, class DefaultType, typename = void>
struct GetType
{
using type = DefaultType;
};
template <class T, template <class> class Slot, class DefaultType>
struct GetType<T, Slot, DefaultType, std::void_t<Slot<T>>>
{
using type = Slot<T>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<T, Slot, DefaultType>::type;
демо здесь
Хитрость здесь заключается в использовании последнего параметра шаблона с аргументом по умолчанию void
. Благодаря тому, как работает сопоставление частичных специализаций шаблонов классов (см., Например, этот ответ ), специализация будет выбрана, только если Slot<T>
является допустимым типом. Обратите внимание, что вышеуказанное решение требует C ++ 17. Если вам нужно оставаться в C ++ 14 (что, вероятно, нет, учитывая, что ваш собственный пример опирается на C ++ 17), вы можете, например, предоставить собственную реализацию void_t
(как объяснено здесь ):
template <typename... T> struct make_void { using type = void; };
template <typename... T> using void_t = typename make_void<T...>::type;