模板化 ID 生成器定义明确的行为?
Templated Id Generator well defined behavior?
我想避免将 Id
分配给所有需要 Id
的 类,因此我编写了一个小型模板化 ID 生成器。
BaseIdGenerator::makeUniqueId
只是 returns 一个新的 Id
,每次调用时:
class BaseIdGenerator {
protected:
static inline Id makeUniqueId() {
static Id nextId = 0;
return nextId++;
}
};
为了将 Id
分配给单个 classes,class 只是作为模板参数传递给 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();
每 class 这将调用 makeUniqueId()
一次(由于线程安全的局部静态变量,即使在自 C++11 以来的多线程应用程序中)
实际操作如下:
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++ 中定义明确的行为吗?
- 多次使用
getId()
会破坏此功能吗? (多个源文件等)
- 这种行为是否标准化,所以在每台机器上的输出都是相同的,并且在网络应用程序中它会按预期工作?
- Is this well defined behavior in C++?
是的,定义明确。
- Will many uses of
getId()
break this functionality? (Multiple source files etc.)
不,一般情况下不会。但是多线程访问可能会导致竞争条件。
- Is this behavior standardized, so on every machine the output will be the same and in a network application it will work as expected?
是的,行为是标准化的。
除了字节序外,我没有看到对网络应用程序的干扰。当然,生成的 ID 将与生成它们的主机具有相同的字节顺序。为避免这种情况,请使用 htonl()
函数(假设 Id
是长整数类型)。
This will call makeUniqueId() exactly one time per class (even at multithreaded applications since C++11 due to the thread safe local static variables)
局部static
变量的初始化是线程安全的。 不修改它们,所以只要在函数中有一个局部静态变量就可以确保它在多线程程序中只构造一次。您手动执行的任何其他操作都容易出现竞争条件,并且需要您进行同步。例如,如果您从多个线程同时调用 makeUniqueId()
,您上面的内容很容易出现竞争条件。
This wikipedia article 很好地解释了静态局部变量构造的工作原理以及它们如何防止多线程访问。但再次注意,受语言和编译器保护的只是静态局部变量的构造。
Is this well defined behavior in C++?
正如您现在所拥有的那样,假设您的所有代码都可以编译,是的,这是定义明确的行为,它不会像您所想的那样违反 ODR。但是,如果您在一个实现文件中将 getId()
and/or IdGenerator
class 特化,而将 link 特化到另一个没有看到该特化的文件,那么您就是违反 ODR,因为现在系统中对同一事物有两种定义(一种是专门的,另一种不是)。在这种情况下,编译器甚至不需要发出任何诊断警告。因此,尽管这按原样工作,但要小心并了解 ODR。有关详细信息,请参阅 http://en.cppreference.com/w/cpp/language/definition
Will many uses of getId() break this functionality? (Multiple source files etc.)
您可以使用 getId()
多次。但是没有指定静态变量初始化顺序,所以 getId()
可能不会一直 return 相同的值。但是,如果您没有种族和 ODR 违规(如上所述),您将拥有不同的价值观
Is this behavior standardized, so on every machine the output will be the same and in a network application it will work as expected?
静态变量初始化顺序在 t运行slation 单元中未指定,所以我想说它在每台机器上可能都不一样。我并没有真正了解这对于网络应用程序会如何改变。在程序启动之前初始化值的代码是 运行。因此,所有 ID 都将在任何网络 I/O 之前设置(假设您在 main()
之前调用的静态函数中没有网络 I/O 是 运行,在这种情况下getId()
的值甚至可能未初始化)
我想避免将 Id
分配给所有需要 Id
的 类,因此我编写了一个小型模板化 ID 生成器。
BaseIdGenerator::makeUniqueId
只是 returns 一个新的 Id
,每次调用时:
class BaseIdGenerator {
protected:
static inline Id makeUniqueId() {
static Id nextId = 0;
return nextId++;
}
};
为了将 Id
分配给单个 classes,class 只是作为模板参数传递给 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();
每 class 这将调用 makeUniqueId()
一次(由于线程安全的局部静态变量,即使在自 C++11 以来的多线程应用程序中)
实际操作如下:
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++ 中定义明确的行为吗?
- 多次使用
getId()
会破坏此功能吗? (多个源文件等) - 这种行为是否标准化,所以在每台机器上的输出都是相同的,并且在网络应用程序中它会按预期工作?
- Is this well defined behavior in C++?
是的,定义明确。
- Will many uses of
getId()
break this functionality? (Multiple source files etc.)
不,一般情况下不会。但是多线程访问可能会导致竞争条件。
- Is this behavior standardized, so on every machine the output will be the same and in a network application it will work as expected?
是的,行为是标准化的。
除了字节序外,我没有看到对网络应用程序的干扰。当然,生成的 ID 将与生成它们的主机具有相同的字节顺序。为避免这种情况,请使用 htonl()
函数(假设 Id
是长整数类型)。
This will call makeUniqueId() exactly one time per class (even at multithreaded applications since C++11 due to the thread safe local static variables)
局部static
变量的初始化是线程安全的。 不修改它们,所以只要在函数中有一个局部静态变量就可以确保它在多线程程序中只构造一次。您手动执行的任何其他操作都容易出现竞争条件,并且需要您进行同步。例如,如果您从多个线程同时调用 makeUniqueId()
,您上面的内容很容易出现竞争条件。
This wikipedia article 很好地解释了静态局部变量构造的工作原理以及它们如何防止多线程访问。但再次注意,受语言和编译器保护的只是静态局部变量的构造。
Is this well defined behavior in C++?
正如您现在所拥有的那样,假设您的所有代码都可以编译,是的,这是定义明确的行为,它不会像您所想的那样违反 ODR。但是,如果您在一个实现文件中将 getId()
and/or IdGenerator
class 特化,而将 link 特化到另一个没有看到该特化的文件,那么您就是违反 ODR,因为现在系统中对同一事物有两种定义(一种是专门的,另一种不是)。在这种情况下,编译器甚至不需要发出任何诊断警告。因此,尽管这按原样工作,但要小心并了解 ODR。有关详细信息,请参阅 http://en.cppreference.com/w/cpp/language/definition
Will many uses of getId() break this functionality? (Multiple source files etc.)
您可以使用 getId()
多次。但是没有指定静态变量初始化顺序,所以 getId()
可能不会一直 return 相同的值。但是,如果您没有种族和 ODR 违规(如上所述),您将拥有不同的价值观
Is this behavior standardized, so on every machine the output will be the same and in a network application it will work as expected?
静态变量初始化顺序在 t运行slation 单元中未指定,所以我想说它在每台机器上可能都不一样。我并没有真正了解这对于网络应用程序会如何改变。在程序启动之前初始化值的代码是 运行。因此,所有 ID 都将在任何网络 I/O 之前设置(假设您在 main()
之前调用的静态函数中没有网络 I/O 是 运行,在这种情况下getId()
的值甚至可能未初始化)