处理(否则)包含C ++ 11随机类的随机生成器调用的常量函数的正确方法是什么?您是否应该放弃函数的常量标志,还是将生成器和分布声明为类的可变元素? 最小的例子(不编译)可能是:
#include <random>
class foo
{
std::mt19937 MyGenerator;
std::normal_distribution<precision_type> gauss;
double get_rnd() const {return gauss(MyGenerator);}
};
答案 0 :(得分:11)
这实际上取决于您为const
成员访问提供的语义。
对于标准类,从多个线程同时调用const
成员是线程安全的。让成员const
改变RNG会破坏这一点,除非RNG完全是线程安全的(非const
使用)。
您不必以同样的方式设计您的课程,但其他开发人员可能会发现发现无法安全阅读的课程会让您感到困惑。 (同时调用const
成员函数)。
一种选择是提供两种变体 - 使用内部存储的RNG的非const版本,以及通过非const引用接受RNG的const
版本。 (第一个可以调用第二个,不需要const_cast
)。这实现了&#34;只为你需要的东西付费&#34;指导w.r.t线程安全,因为如果每个线程提供一个线程本地RNG实例,则多个线程可以安全地使用该对象。它还允许使用模拟RNG实现进行测试,这可能更有价值。
答案 1 :(得分:6)
这取决于你想要达到的目标,典型的策略包括:
暴露可变性。只是不要将方法标记为const
。
外部化以暴露可变性。将可变元素(这里是生成器和分布)作为方法参数传递,暴露内部可变性的使用,调用者负责任何线程安全隐含。
实施&#34;逻辑&#34;常量性。如果不考虑使用随机性破坏类的逻辑常量,则可以简单地将生成器和分布声明为mutable
;谨防线程安全影响(如果需要,即在多线程应用程序中,使用mutable
互斥锁)
您选择哪种替代方案取决于您要实现的语义。
答案 2 :(得分:2)
mutable
关键字是针对此类案例设计的。当您使用此修饰符标记随机生成器实例字段时,即使在封闭类的const
方法中也允许更改其状态。
一般来说,这似乎是一个灰色区域,具体取决于您的类所代表的概念类型。如果生成器的状态在概念上与该类的状态无关,则该解决方案是可以的。否则你应该重新考虑设计 - 如果生成器的状态是相关的,那么使用它的方法不应该是const
。
答案 3 :(得分:1)
我认为这取决于您的使用案例。如果你想要一些完全确定的行为,你必须删除const标志以确保你的类的状态不能改变然后它不会被改变。如果编写必须可重复测试的安全相关代码或代码,这可能很重要。