I am trying to generate unique IDs from template template parameters. I tried this function
inline size_t g_id = 1;
template<template<typename> typename T>
inline size_t GetID()
{
static size_t id = g_id++;
return id;
}
it works fine until used with alias templates
template<template<typename> typename T>
inline void print()
{
std::cout << GetID<T>() << "\n";
}
template<typename T>
struct S {};
struct W1 { template<typename A> using type = S<A>; };
struct W2 { template<typename A> using type = S<A>; };
int main()
{
print<S>();
print<W1::type>();
print<W2::type>();
std::cin.get();
}
MSVC
1
2
3
clang
1
2
3
gcc
1
1
1
Is any compiler correct here or is there UB somewhere?
After reading some of the material linked from Davis Herring`s comment CG1286, an alias template does not need to have the same template name as the underlying template. To me this seems like it could go both ways so are all compilers compliant here?
With that I have come up with a different way to generate IDs from template template parameters which should avoid this problem but has some compromises. Requires that you specialize the template with a Tag type and create a static method which retrieves your ID.
Implementation
inline size_t g_id = 1;
template<typename T>
inline size_t GenerateID()
{
static size_t id = g_id++;
return id;
}
struct Tag {};
template<template<typename...> typename T, typename... Args, typename = decltype(sizeof(T<Args...>))>
inline size_t get_id_imp(int)
{
return T<Args...>::GetID();
}
template<template<typename...> typename T, typename... Args, std::enable_if_t<sizeof...(Args) < 16, bool> = true>//16 = max template args
inline size_t get_id_imp(...)
{
return get_id_imp<T, Args..., Tag>(0);
}
template<template<typename...> typename T, typename... Args, std::enable_if_t<sizeof...(Args) >= 16, bool > = true>
inline size_t get_id_imp(...)
{
return 0;
}
template<template<typename...> typename T>
inline size_t GetID()
{
return get_id_imp<T, Tag>(0);
}
Use
template<typename T>
struct X {};
template<> struct X<Tag> { static size_t GetID() { return GenerateID<X>(); } };
template<template<typename...> typename T>
inline void print()
{
std::cout << GetID<T>() << "\n";
}
For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.
Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.
Template non-type arguments in C++It is also possible to use non-type arguments (basic/derived data types) i.e., in addition to the type argument T, it can also use other arguments such as strings, function names, constant expressions, and built-in data types.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
When the name of a non-type template parameter is used in an expression within the body of the class template, it is an unmodifiable prvalue unless its type was an lvalue reference type, or unless its type is a class type (since C++20) .
Type template parameter. In the body of the template declaration, the name of a type parameter is a typedef-name which aliases the type supplied when the template is instantiated. There is no difference between the keywords class and typename in a type template parameter declaration.
There is a function that has been released recently that will help you a lot with this need, the uniqueString () function. Having a unique value each time you perform a deployment is not very useful when dealing with ARM template.
Every template is parameterized by one or more template parameters, indicated in the parameter-list of the template declaration syntax: template < parameter-list > declaration Each parameter in parameter-list may be: a non-type template parameter;
There is no UB here. The template GetID
is instantiated once for each unique template argument, but GCC wrongly treats the alias templates as the template they alias itself, because they are equivalent here, as Davis Herring pointed out.
I think the simplest general solution is to pass the argument types in the alias templates through another alias template that makes them dependent names.
template<class Type> struct typeAlias { using AliasedType = Type; };
template<class Type> using AliasType = typename typeAlias<Type>::AliasedType;
template<typename T>
struct S {};
struct W1 { template<typename A> using type = S<AliasType<A>>; };
struct W2 { template<typename A> using type = S<AliasType<A>>; };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With