我想避免为需要Id
的所有类分配BaseIdGenerator::makeUniqueId
,所以我写了一个小的模板化id生成器。
Id
只会返回一个新的class BaseIdGenerator {
protected:
static inline Id makeUniqueId() {
static Id nextId = 0;
return nextId++;
}
};
:
Id
为了将IdGenerator
分配给各个类,该类只是作为模板参数传递给template <typename T>
Id getId();
template <typename T>
class IdGenerator final : public BaseIdGenerator {
static Id id;
template <typename Type>
friend Id getId();
};
template <typename T>
inline Id getId() {
return IdGenerator<T>::id;
}
template <typename T>
Id IdGenerator<T>::id = IdGenerator<T>::makeUniqueId();
:
makeUniqueId()
这将每个类调用int main() {
std::cout << getId<int>() << std::endl; // prints 0
std::cout << getId<bool>() << std::endl; // prints 1
std::cout << getId<char>() << std::endl; // prints 2
}
一次(即使是因为线程安全的本地静态变量,因为C ++ 11以来的多线程应用程序)
在行动中,这看起来像这样:
getId()
这可以按预期工作。
答案 0 :(得分:3)
这将在每个类中调用makeUniqueId()一次(因为线程安全的本地静态变量,即使在C ++ 11以来的多线程应用程序中)
本地static
变量的初始化是线程安全的。 不修改它们,所以在函数中只有一个静态变量local将确保它在多线程程序中只构造一次且只构造一次。您手动执行的任何其他操作都容易出现竞争条件,并且需要您进行同步。例如,如果您从多个线程同时调用makeUniqueId()
,则上面的内容很容易出现竞争条件。
This wikipedia article很好地解释了静态局部变量构造是如何工作的以及它们如何防止多线程访问。但请注意,只有静态局部变量的构造才受语言和编译器的保护。
这是C ++中明确定义的行为吗?
正如你现在所做的那样,假设所有代码都编译完成,是的,它是明确定义的行为,它不会违反ODR,如果你正在思考的话。但是,如果您在实现文件中专门化getId()
和/或IdGenerator
类并将其链接到另一个看不到该专业化的文件,那么您违反了ODR,因为现在有两个定义系统中的相同的东西(一个是专门的,一个不是)。在这种情况下,甚至不需要编译器警告任何诊断。因此,尽管这样可行,但要小心并了解ODR。有关详情,请参阅http://en.cppreference.com/w/cpp/language/definition
getId()的许多用法是否会破坏此功能? (多个源文件等。)
您可以根据需要多次使用getId()
。但是,未指定静态变量初始化顺序,因此getId()
可能不会始终返回相同的值。但是,如果您没有比赛和ODR违规(如上所述)
这种行为是否标准化,因此在每台机器上输出都是相同的,在网络应用程序中它会按预期工作?
在翻译单元中未指定静态变量初始化顺序,因此我想说它在每台机器上可能都不一样。我并没有真正了解网络应用程序如何改变。初始化值的代码在程序启动之前运行。因此,所有ID都将在任何网络I / O之前设置(假设在运行main()
之前调用的静态函数中没有网络I / O,在这种情况下{{1}的值之一甚至可能没有初始化)
答案 1 :(得分:2)
- 这是C ++中明确定义的行为吗?
是的,它定义得很好。
getId()
的许多用途是否会破坏此功能? (多个源文件等。)
不,不是一般的。但多线程访问可能会导致竞争条件。
- 这种行为是否标准化,所以在每台机器上输出都是相同的,在网络应用程序中它会按预期工作?
是的,行为是标准化的。
除了字幕外,我还没有看到对网络应用的干扰。当然,生成的ID将与生成它们的主机具有相同的endianess。为了避免使用htonl()
函数(假设Id
是一个长整数类型)。