Distinguish a template parameter as a nested template

I have a template function getId()

and it can be used like getId< SomeType >()

and getId< Some< NestedType >>()

. I have to differentiate them somehow.

template<typename TRequest>
ParameterId getId()  // #1
{
    return tEParameterId_None;
}

template<template <class> class TRequest, class TType>
ParameterId getId()  // #2
{
    return TType::paramId;
}

template<TRequest<TType>>
ParameterId getId()  // #3, not working
{
    return TType::paramId;
}

ParameterId none = getId<SomeType>();                   // #1 will be called
ParameterId still_none = getId<Some<NestedType>>();     // #1 will be called, but I want #3
ParameterId some_other = getId<SomeType, NestedType>(); // #2 will be called

      

My question is, how do I specify # 3getId()

a template function that getId< Some < NestedType > >()

invokes exactly the 3D option? Or what compile time pattern mask can I distinguish between the nested pattern?

Because in general code notation is used, for example Some< NestedType >

, and I don't want to change it and call it like getId< SomeType, NestedType >()

- it will be inconsistent.

+3


source to share


2 answers


You can use a special type to determine if a type is a template:

template <class> 
struct is_template : std::false_type {};

template <template <class...> class T, typename ...Args>
struct is_template<T<Args...>> : std::true_type {};

      

And use std::enable_if

to select the correct overloading (include overloading if the type is template, otherwise allow otherwise):

template<class T>
typename std::enable_if<is_template<T>::value, int>::type getId()  // #1
{
    std::cout << "#1";
    return 42;
}

template<class T>
typename std::enable_if<!is_template<T>::value, int>::type getId()  // #2
{
    std::cout << "#2";
    return 42;
}

      



Using:

int main()
{
    getId<int>();               // Calls #2
    getId<std::vector<int>>();  // Calls #1
}

      

Live Demo

+3


source


This will recursively apply your rule, unwinding templates, until you navigate to the non-template parameter:

template<class T>
struct param_id : std::integral_constant< int, T::paramId > {};
template<template<class...>class Z, class T, class... Args>
struct param_id<Z<T,Args...>> : param_id<T> {};

template<class T>
constexpr int getId() { return param_id<T>::value; }

      

constexpr

is optional, but in C ++ 11 means it getId<SomeType>()

is evaluated at compile time in many contexts.

This means there getId< std::vector< std::vector< SomeType > > >()

is getId< SomeType >()

. This also means that if you want other primitive types to have a fixed identifier, you can manually specify a specialist param_id

.

If you want an industrial strength solution, I would go as far as creating an ADL based search schema, but this is a more complex question.



std::integral_constant

- another C ++ 11ism. If you are lacking support, replace

template<class T>
struct param_id : std::integral_constant< int, T::paramId > {};

      

from

template<class T>
struct param_id { enum {value=T::paramId}; };

      

+3


source







All Articles