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

B.D*_*B.D 8 c++ templates language-lawyer type-alias c++17

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

inline size_t g_id = 1;

template<template<typename> typename T>
inline size_t GetID()
{
    static size_t id = g_id++;
    return id;
}
Run Code Online (Sandbox Code Playgroud)

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

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();
}
Run Code Online (Sandbox Code Playgroud)

MSVC

1
2
3
Run Code Online (Sandbox Code Playgroud)

1
2
3
Run Code Online (Sandbox Code Playgroud)

海湾合作委员会

1
1
1
Run Code Online (Sandbox Code Playgroud)

这里有任何编译器是正确的还是有 UB 某处?

更新

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

为此,我想出了一种从模板模板参数生成 ID 的不同方法,这应该可以避免这个问题,但有一些妥协。要求您使用标签类型专门化模板并创建一个检索您的 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);
}
Run Code Online (Sandbox Code Playgroud)

使用

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";
}
Run Code Online (Sandbox Code Playgroud)

Rad*_*ich 1

这里没有UB。对于每个唯一的模板参数,模板GetID都会实例化一次,但 GCC 错误地将别名模板视为它们为自己别名的模板,因为它们在这里是等效的,正如 Davis Herring 指出的那样

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

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>>; };
Run Code Online (Sandbox Code Playgroud)