我看过很多文章,解释了如何为一个类生成唯一的ID。
在我的情况下,ID是由用户选择的(出于各种原因),但是我想确保在不同的类中没有两次使用过ID。
我将问题简化为以下代码:
struct A {}; struct B {};
template <typename T> struct traits {};
template <> struct traits<A> { static constexpr size_t id() { return 0; }}
template <> struct traits<B> { static constexpr size_t id() { return 1; }}
现在,我有一种简单的方法来确保没有人添加带有重复ID的特质:
struct C {};
template <> struct traits<C> { static constexpr size_t id() { return 1; // this should static_assert ! }}
我可以使用C ++ 11,并且我不想滥用预处理器。
如果可能的话,该解决方案应该不需要专用于特征的代码中的任何特殊内容(即,如果可以通过查看已经存在的专业来从外部进行检查,那就太好了。)
谢谢
答案 0 :(得分:3)
这是一个主意,我不确定它对您是否适用:
#include <cstddef>
struct A {}; struct B {}; struct C {};
template <size_t Id> constexpr size_t getId() { return Id; }
template <typename T> struct traits {};
template size_t getId<0>();
template <> struct traits<A> { static constexpr size_t id() { return getId<0>(); }};
template size_t getId<1>();
template <> struct traits<B> { static constexpr size_t id() { return getId<1>(); }};
/* This fails to compile
template size_t getId<1>();
template <> struct traits<C> { static constexpr size_t id() { return getId<1>(); }};
*/
int main() { return 0; }
这依赖于使用函数的显式模板实例化。显然,陷阱在于您可能忘记添加此实例化并仍然使用该函数,然后它就不会失败。您可以定义一些预处理器宏以确保始终使用其定义特征。
编辑:Oliv指出,以上解决方案仅在所有模板实例化发生在同一转换单元中时才有效。此版本可在所有编译单元上使用,尽管它更容易出错(模板参数和返回值必须匹配)。
#include <cstddef>
struct A {}; struct B {}; struct C {};
template <size_t Id> constexpr size_t getId();
template <typename T> struct traits {};
template <> constexpr size_t getId<0>() { return 0; }
template <> struct traits<A> { static constexpr size_t id() { return getId<0>(); }};
template <> constexpr size_t getId<1>() { return 1; }
template <> struct traits<B> { static constexpr size_t id() { return getId<1>(); }};
/* This fails to compile
template <> constexpr size_t getId<1>() { return 1; }
template <> struct traits<C> { static constexpr size_t id() { return getId<1>(); }};
*/
int main() { return 0; }
编辑2:由于发布此答案时发现令人惊讶的行为,我发布了问题Why does explicit template instantiation not break ODR?。有关无法编译的内容的详细信息,请参见此处。
答案 1 :(得分:1)
这听起来很糟糕,但并不完美,但是您可以通过从串联的__LINE__
和__FILE__
宏定义中计算一些哈希函数来强制实现非常好的唯一性。
constexpr std::uint32_t calculate_hash(std::uint32_t seed, const char data[])
{
//some hashing algorithm
}
#define CALCULATE_HASH_FROM_LINE() calculate_hash(__LINE__, __FILE__)
struct A {};
struct B {};
struct C {};
template <typename T> struct traits {};
template <> struct traits<A> { static constexpr size_t id() { return CALCULATE_HASH_FROM_LINE(); }};
template <> struct traits<B> { static constexpr size_t id() { return CALCULATE_HASH_FROM_LINE(); }};
template <> struct traits<C> { static constexpr size_t id() { return CALCULATE_HASH_FROM_LINE(); }};
完整代码:http://coliru.stacked-crooked.com/a/c1805baf9863b238
优点:
缺点:
其他方法是使用宏提取类型名称:
constexpr std::uint32_t calculate_hash(std::uint32_t seed, const char data[])
{
//some hashing algorithm
}
#define CALCULATE_HASH(T) calculate_hash(29, #T)
struct A {};
struct B {};
struct C {};
template <typename T> struct traits {};
template <> struct traits<A> { static constexpr size_t id() { return CALCULATE_HASH(A); }};
template <> struct traits<B> { static constexpr size_t id() { return CALCULATE_HASH(B); }};
template <> struct traits<C> { static constexpr size_t id() { return CALCULATE_HASH(C); }};
完整代码:http://coliru.stacked-crooked.com/a/46b00f9aea039c5b 此方法具有与以前相同的优缺点,但另外:
优点:
缺点:
::A
将产生与A
因此,这两种方式在许多情况下都可能有效,但本质上是错误的。 C ++非常需要编译时反射:/.