我想创建不同的类型,它们是 uint32_t 但从编译器的角度来看不同——它们只能进行比较并分配给完全相同类型的值。这是我想要实现的示例代码:
TextureResourceId t1 = 1000, t2 = 2000;
PipelineResourceId p1 = 1000, p2 = 2000;
BufferResourceId b1 = 1000, b2 = 1000;
if (t1 == t2) // OK!
if (t1 == p1) // Compiler error!
if (t1 == b1) // Compiler error!
Run Code Online (Sandbox Code Playgroud)
我知道我可以做一些预处理器魔法来实现这一点:
#define CREATE_RESOURCE_ID(NAME) \
class NAME { \
public: \
NAME(uint32_t value_): value(value_) {} \
bool operator==(const NAME &rhs) { return value == rhs.value; } \
private: \
uint32_t value; \
};
CREATE_RESOURCE_ID(TextureResourceId);
CREATE_RESOURCE_ID(BufferResourceId);
CREATE_RESOURCE_ID(PipelineResourceId);
int main() {
TextureResourceId tex1(1000), tex2(2000);
BufferResourceId buf1(1000);
PipelineResourceId pip1(1000);
if (tex1 == tex2) {}
if (buf1 == tex1) {}
if (tex1 == pip1) {}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但我想知道是否有更 C++ 的方式来执行此操作(例如,使用继承或某种带有枚举类的语法)
您可以创建模板化原型并制作其类型标记版本。
我不久前做了这个,还没有使用它,所以没有测试,但我认为这个想法应该是合理的:
template<typename Type, typename Number>
class typed_id
{
public:
explicit typed_id(Number n): n(n) {}
typed_id(typed_id const& id): n(id.n) {}
typed_id& operator=(typed_id const& n) { this->n = n.n; return *this; }
bool operator<(typed_id id) const { return this->n < id.n; }
bool operator>(typed_id id) const { return this->n > id.n; }
bool operator==(typed_id id) const { return this->n == id.n; }
bool operator!=(typed_id id) const { return this->n != id.n; }
private:
Number n;
};
// tag types to differentiate each
// instantiation of the archetype
struct TextureResourceIdTag{};
struct PipelineResourceIdTag{};
struct BufferResourceIdTag{};
// actual types
using TextureResourceId = typed_id<TextureResourceIdTag, std::size_t>;
using PipelineResourceId = typed_id<PipelineResourceIdTag, std::size_t>;
using BufferResourceId = typed_id<BufferResourceIdTag, std::size_t>;
int main()
{
TextureResourceId t1{1000};
TextureResourceId t2{2000};
PipelineResourceId p1{1000};
PipelineResourceId p2{2000};
BufferResourceId b1{1000};
BufferResourceId b2{2000};
if (t1 == t2) {} // OK!
if (t1 == p1) {} // Compiler error!
if (t1 == b1) {} // Compiler error!
}
Run Code Online (Sandbox Code Playgroud)
由于您似乎只需要检查身份,因此 anenum应该是自然的选择,正如您所提到的,enum class可用于确保基础类型是uint32_t:
enum class TextureResourceId: uint32_t
{
id1 = 1000,
id2 = 2000
};
enum class PipelineResource: uint32_t
{
id1 = 1000,
id2 = 2000
};
enum class BufferResourceId: uint32_t
{
id1 = 1000,
id2 = 2000
};
Run Code Online (Sandbox Code Playgroud)
值已初始化,如示例所示:
TextureResourceId t1 = TextureResourceId::id1, t2 = TextureResourceId::id2;
PipelineResourceId p1 = PipelineResourceId::id1, p2 = PipelineResourceId::id2;
BufferResourceId b1 = BufferResourceId::id1, b2 = BufferResourceId::id2;
Run Code Online (Sandbox Code Playgroud)
编译器应按照指定的方式运行,即允许在同一类内比较 ID,但不允许在不同类之间进行比较。
注意:如果您不想在枚举类中显式声明所有可能的身份,身份也可以从整数构造,例如TextureResourceId(1000)。