B.D
B.D

Reputation: 118

Is generating unique ID from template template parameters UB?

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?

Update

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";
}

Upvotes: 8

Views: 733

Answers (1)

Radoslav Voydanovich
Radoslav Voydanovich

Reputation: 481

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

Upvotes: 1

Related Questions