typedef X <T>= T :: UserType1, но если не применимо, typedef X <T>= UserType2 - PullRequest
0 голосов
/ 15 апреля 2019

Вот MCVE (не компилируется): -

#include <iostream>
#include <type_traits>
//-- library ---
template<class T,template<class>class Slot,class DefaultType> 
  class GetType{
    template <typename C> static Slot<T> check( Slot<T>*);
    template <typename> static DefaultType check(...);
    public: using type=decltype(check<T>());
}; 
template<class T,template<class>class Slot,class DefaultType>
  using X = typename GetType<T,Slot,DefaultType>::type; 

Вот его использование: -

//--- user defined ---
class B {public: using MyType=int;};
class C{};
template<class T> using SlotCustom = typename T::MyType;
int main(){
    using ShouldInt=X< B ,SlotCustom ,long>; //B::Mytype =int     , result:int
    using ShouldLong=X< C ,SlotCustom ,long>;//C::Mytype not exist, result:long
    std::cout<< std::is_same_v<ShouldInt, int> <<std::cout; //should true
    std::cout<< std::is_same_v<ShouldLong, long> <<std::cout; //should true
}

Моя цель - создать библиотеку typedef X< Param1 ,SlotCustom ,DefaultType>, что в качестве следующего псевдокода означает: -

if ( SlotCustom<Param1> has meaning) return "SlotCustom<Param1>" ;
else return "DefaultType"; //i.e. by default 

Как это сделать?

Здесь аналогичный вопрос.
Основное отличие состоит в том, что X<T> может быть только bool, а многие вещи жестко закодированы.

Я новичок в шаблонной специализации. Решение может быть очевидным, но я не могу его найти.

1 Ответ

1 голос
/ 15 апреля 2019

Если я правильно понимаю ваш вопрос, то ваш подход можно заставить работать, например

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;
...