【问题标题】:Is generating unique ID from template template parameters UB?是否从模板模板参数 UB 生成唯一 ID?
【发布时间】:2021-12-15 15:18:30
【问题描述】:

我正在尝试从模板模板参数生成唯一 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

海合会

1
1
1

这里有没有正确的编译器,或者某处有 UB?

更新

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

因此,我想出了一种不同的方法来从模板模板参数生成 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";
}

【问题讨论】:

标签: c++ templates c++17 language-lawyer type-alias


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢,现在有一个通用的解决方案可以使 GCC 符合我想知道是否还有一个通用的解决方案来获取 GCC 的行为。那就是别名模板等同于别名本身。我认为这是模板模板 ID 生成器所需的行为。
  • @B.D 我认为您不想用这些语义重现这种行为。根据模板实参和形参列表的不同配置,它们可能被视为等价或不等价,而从使用此类别名模板作为模板实参的角度来看,您不容易看到这一点,尤其是在更复杂的程序中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-02
  • 2021-12-21
  • 1970-01-01
  • 2011-07-05
  • 2021-09-28
  • 1970-01-01
相关资源
最近更新 更多