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.
source to share
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
}
source to share
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}; };
source to share