编译时常量id

Dav*_*vid 43 c++ templates template-meta-programming

鉴于以下内容:

template<typename T>
class A
{
public:
    static const unsigned int ID = ?;
};
Run Code Online (Sandbox Code Playgroud)

我希望ID为每个T生成一个唯一的编译时间ID.我考虑过__COUNTER__并且增强PP库但到目前为止都没有成功.我怎样才能做到这一点?

编辑:ID必须可用作switch语句中的大小写

Edit2:基于静态方法或成员地址的所有答案都不正确.尽管它们确实创建了唯一的ID,但它们在编译时未得到解析,因此不能用作switch语句的情况.

MSN*_*MSN 11

假设符合标准的编译器(关于一个定义规则),这就足够了:

template<typename T>
class A
{
public:
    static char ID_storage;
    static const void * const ID;
};

template<typename T> char A<T>::ID_storage;
template<typename T> const void * const A<T>::ID= &A<T>::ID_storage;
Run Code Online (Sandbox Code Playgroud)

从C++标准3.2.5一个定义规则[basic.def.odr](大胆强调我的):

...如果D是模板并且是在多个翻译单元中定义的,那么上面列表中的最后四个要求将应用于模板定义(14.6.3)中使用的模板封闭范围中的名称,以及实例化时的从属名称(14.6.2).如果D的定义满足所有这些要求,那么程序应该表现得就像D的单个定义一样.如果D的定义不满足这些要求,则行为是不确定的.

  • 还不够,每个编译单元都会获得自己的值副本.为了工作,应根据一个定义规则在CPP中定义它们,但由于它们是模板,因此无法完成. (8认同)
  • @bdonlan:为了什么?一个定义规则?或者在头文件中定义的静态对象将在包含它们的每个翻译单元中定义?请记住,`#include`与复制和粘贴文件内容没有什么不同...... (2认同)

Gui*_*cot 9

我通常使用的是:

template<typename>
void type_id(){}

using type_id_t = void(*)();
Run Code Online (Sandbox Code Playgroud)

由于函数的每个实例都有自己的地址,因此您可以使用该地址来标识类型:

// Work at compile time
constexpr type_id_t int_id = type_id<int>;

// Work at runtime too
std::map<type_id_t, std::any> types;

types[type_id<int>] = 4;
types[type_id<std::string>] = "values"s

// Find values
auto it = types.find(type_id<int>);

if (it != types.end()) {
    // Found it!
}
Run Code Online (Sandbox Code Playgroud)


J. *_*eja 5

可以使用此答案中的代码从字符串生成编译时哈希。

如果您可以修改模板以包含一个额外的整数并使用宏来声明变量:

template<typename T, int ID> struct A
{
    static const int id = ID;
};

#define DECLARE_A(x) A<x, COMPILE_TIME_CRC32_STR(#x)>
Run Code Online (Sandbox Code Playgroud)

使用此宏进行类型声明,id 成员包含类型名称的哈希值。例如:

int main() 
{
    DECLARE_A(int) a;
    DECLARE_A(double) b;
    DECLARE_A(float) c;
    switch(a.id)
    {
    case DECLARE_A(int)::id:
        cout << "int" << endl;
        break;
    case DECLARE_A(double)::id:
        cout << "double" << endl;
        break;
    case DECLARE_A(float)::id:
        cout << "float" << endl;
        break;
    };
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当类型名称转换为字符串时,对类型名称文本的任何修改都会产生不同的 id。例如:

static_assert(DECLARE_A(size_t)::id != DECLARE_A(std::size_t)::id, "");
Run Code Online (Sandbox Code Playgroud)

另一个缺点是可能发生哈希冲突。

  • 为了将碰撞概率降低到舒适的水平,您可以使用 [constexpr MD5 函数](https://github.com/elbeno/constexpr/blob/master/src/include/cx_md5.h) 和 [__int128 类型](https:// gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html)获取 ID。 (3认同)

Pub*_*bby 3

使用静态函数的内存地址。

template<typename T>
class A  {
public:
    static void ID() {}
}; 
Run Code Online (Sandbox Code Playgroud)

(&(A<int>::ID))将不同于(&(A<char>::ID))等等。

  • 为了获取静态常量积分的地址(请注意缺少 const 修饰符),必须在适当的 CPP 文件中定义此类积分,而这是无法使用模板完成的。否则是未定义的行为;如果在头文件中声明静态,您将在不同的编译单元获得不同的地址。 (3认同)