类型的替代id生成器

sky*_*ack 15 c++ shared-libraries c++17

在我的一个项目中,我有一个类似于这个类型的ID生成器:

class Family {
    static std::size_t identifier;

    template<typename...>
    static std::size_t family() {
        static const std::size_t value = identifier++;
        return value;
    }

public:
    template<typename... Type>
    inline static std::size_t type() {
        return family<std::decay_t<Type>...>();
    }
};

std::size_t Family::identifier{};
Run Code Online (Sandbox Code Playgroud)

用法:

const auto id = Family::type<FooBar>();
Run Code Online (Sandbox Code Playgroud)

它适用于我的目的,但它有一些局限性.最令人讨厌的问题(问题的目的)是,当链接到共享库的可执行文件使用它时,它会失败,如果它们都尝试创建标识符的话.结果通常是第n个标识符跨越边界分配给不同类型,因为每个共享库都保持它们自己的独立Family::identifier.

一些共享库的人指出,一个更可靠的解决方案将被赞赏,但没有提出一个没有破坏性能(几乎所有这些都引入容器,找到功能和内存分配).

是否有任何替代方法可以解决上述限制而不会失去当前设计的性能?

我搜索了SO并找到了一些有趣的答案.其中很多都是几年了.我想探索最新版标准的解决方案,只要现有类的接口保持不变.
是最有趣的一个.它使用静态成员的地址来实现相同的目的,但它不符合顺序生成的标识符的想法

注意:不幸的是,使用RTTI不是一个选项.

注意:必须按顺序生成ID,并从0开始,如上面的解决方案所示.

Joh*_*nck 5

出现您的问题是因为您的头文件中有此行:

std::size_t Family::identifier{};
Run Code Online (Sandbox Code Playgroud)

因此它最终出现在每个翻译单元中。相反,您需要将其存储移动到仅编译一次的 .cpp 源文件,或许可以移动到它自己的共享库中。identifier然后程序中将只有一个 实例,并且它将按您的预期工作。

您还可以在头文件中identifier从类static变量转变为全局变量extern(如上所述,在单个 .cpp 文件中定义它)。

如果您有 C++17 或更高版本,您还可以尝试:

inline std::size_t Family::identifier{};
Run Code Online (Sandbox Code Playgroud)

虽然该语言不能保证(甚至提及)当您跨共享库边界使用此新功能时会发生什么,但它确实可以在我的机器上运行。

  • 那么这在仅标头库中是不可能的。C++ 不是这样工作的。实际上,在头文件中执行此操作违反了 ODR,这就是您获得重复标识符的原因:未定义的行为。 (2认同)