是否从模板模板参数 UB 生成唯一 ID?

Is generating unique ID from template template parameters UB?

我正在尝试从模板模板参数生成唯一 ID。我试过这个功能

inline size_t g_id = 1;

template<template<typename> typename T>
inline size_t GetID()
{
    static size_t id = g_id++;
    return id;
}

在与别名模板一起使用之前它工作正常

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

铿锵

1
2
3

gcc

1
1
1

此处是否有任何编译器正确或某处有 UB?

更新

阅读 Davis Herring 的评论 CG1286 中的一些 material 链接后,别名模板不需要与基础模板具有相同的模板名称。在我看来,这似乎是双向的,所以所有编译器都兼容吗?

据此,我想出了一种不同的方法来从模板模板参数生成 ID,这应该可以避免这个问题,但有一些妥协。要求您使用 Tag 类型专门化模板并创建一个静态方法来检索您的 ID。

实施

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

使用

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

这里没有UB。模板 GetID 为每个唯一的模板参数实例化一次,但 GCC 错误地将别名模板视为它们自己别名的模板,因为它们在这里是等价的,.

我认为最简单的通用解决方案是通过另一个使它们成为依赖名称的别名模板传递别名模板中的参数类型。

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