如何从 uint32_t 创建静态不同的不同类型?

Gas*_*sim 5 c++

我想创建不同的类型,它们是 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++ 的方式来执行此操作(例如,使用继承或某种带有枚举类的语法)

Gal*_*lik 6

您可以创建模板化原型并制作其类型标记版本。

我不久前做了这个,还没有使用它,所以没有测试,但我认为这个想法应该是合理的:

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)


nie*_*sen 4

由于您似乎只需要检查身份,因此 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)

  • @FrançoisAndrieux 谢谢你提到这一点。然而,正如我刚刚添加的注释一样,也可以从整数构造实例,这些实例可用于在运行时定义 ID。 (2认同)