const成员函数中的继承和随机数生成
Inheritance and random number generation in const member function
我有以下class继承结构
#include <random>
class ABC{
protected:
std::mt19937_64 rng;
double a();//calls random correction
virtual double random_correction() const=0;
public:
double x;
explicit ABC(const double x0) :
rng(std::random_device{}()), x(x0){}
ABC(const ABC& other)=default;
ABC& operator=(const ABC& other)=default;
ABC(ABC&& other) noexcept = default;
ABC& operator=(ABC&& other) noexcept = default ;
virtual ~ABC()=default;
virtual void update(const int step_number)=0;//calls a and uses rng
};
class D1 : public ABC{
private:
double y;
double random_correction() const override {return 0;};
public:
D1(const double x0, const double y0):
ABC(x0), y(y0){}
D1(const D1& other)=default;
D1& operator=(const D1& other)=default;
D1(D1&& other) noexcept = default;
D1& operator=(D1&& other) noexcept = default ;
~D1() override=default;
void update(const int step_number) override;
};
class D2 : public D1{
private:
double noise;
double random_correction() const override {
static std::uniform_real_distribution<> u{-noise, noise};
return u(rng); //error here
};
public:
D2(const double x0, const double y0, const double na):
D1(x0,y0), noise(na) {}
D2(const D2& other)=default;
D2& operator=(const D2& other)=default;
D2(D2&& other) noexcept = default;
D2& operator=(D2&& other) noexcept = default ;
~D2() override=default;
};
在 D2 的 random_correction 定义中,出现以下错误:“在此处请求的函数模板特化 'std::uniform_real_distribution::operator()<const std::mersenne_twister_engine<unsigned long, 64, 312, 156, 31, 13043109905998158313, 29, 6148914691236517205, 17, 8202884508482404352, 37, 18444473444759240704, 43, 6364136223846793005> >' 实例化中”。
如果我在所有声明中删除 const 限定符,错误就会消失。
为什么?成员函数中的 const 限定符是否意味着 class 的任何成员都不会被修改?
作为解决方法,我想我可以用这种方式修改 class D2
class D2 : public D1{
private:
double noise;
std::uniform_real_distribution<> u;
double random_correction() const override {
return u(rng); //error always here
};
public:
D2(const double x0, const double y0, const double na):
D1(x0,y0), noise(na) {u.param(std::uniform_real_distribution<>::param_type{-noise, noise});}
D2(const D2& other)=default;
D2& operator=(const D2& other)=default;
D2(D2&& other) noexcept = default;
D2& operator=(D2&& other) noexcept = default ;
~D2() override=default;
};
但是现在我收到错误:“没有匹配函数来调用 'const std::uniform_real_distribution<>' 类型的对象”。我想是因为生成一个随机数会改变 u 的状态(也可能是 rng 的状态)。这个对吗?会员有分配方便吗?
P.S。如果有任何设计错误或效率低下,请指出给我。
生成随机数不会改变 u
的状态。它的状态就是要生成的值的范围。
但它确实改变了 rng
的状态。 Pseudo-random 数字生成器通常是确定性的,这意味着 rng()
由当前状态决定,并且状态会在评估时发生变化,因此下次将生成不同的值。
要解决编译问题,您可以删除函数的 const 声明,也可以将 rng
声明为 mutable
。它仍然会遇到某些问题,例如“并发访问 class 实例会导致数据竞争”。所以你可以考虑重新考虑设计。
RNG 引擎可以从 thread_local
关键字中获益...但是您需要以某种方式设计它,这样它就不会导致可能有点棘手的性能问题。
编辑:在使用 cppreference 签入后,显然 uniform_distribution
可以包含除分布范围之外的额外信息。可能是 RNG 引擎生成的一些数据,因此它可以将它们用于将来的调用,并且不需要经常使用引擎。
我有以下class继承结构
#include <random>
class ABC{
protected:
std::mt19937_64 rng;
double a();//calls random correction
virtual double random_correction() const=0;
public:
double x;
explicit ABC(const double x0) :
rng(std::random_device{}()), x(x0){}
ABC(const ABC& other)=default;
ABC& operator=(const ABC& other)=default;
ABC(ABC&& other) noexcept = default;
ABC& operator=(ABC&& other) noexcept = default ;
virtual ~ABC()=default;
virtual void update(const int step_number)=0;//calls a and uses rng
};
class D1 : public ABC{
private:
double y;
double random_correction() const override {return 0;};
public:
D1(const double x0, const double y0):
ABC(x0), y(y0){}
D1(const D1& other)=default;
D1& operator=(const D1& other)=default;
D1(D1&& other) noexcept = default;
D1& operator=(D1&& other) noexcept = default ;
~D1() override=default;
void update(const int step_number) override;
};
class D2 : public D1{
private:
double noise;
double random_correction() const override {
static std::uniform_real_distribution<> u{-noise, noise};
return u(rng); //error here
};
public:
D2(const double x0, const double y0, const double na):
D1(x0,y0), noise(na) {}
D2(const D2& other)=default;
D2& operator=(const D2& other)=default;
D2(D2&& other) noexcept = default;
D2& operator=(D2&& other) noexcept = default ;
~D2() override=default;
};
在 D2 的 random_correction 定义中,出现以下错误:“在此处请求的函数模板特化 'std::uniform_real_distribution::operator()<const std::mersenne_twister_engine<unsigned long, 64, 312, 156, 31, 13043109905998158313, 29, 6148914691236517205, 17, 8202884508482404352, 37, 18444473444759240704, 43, 6364136223846793005> >' 实例化中”。 如果我在所有声明中删除 const 限定符,错误就会消失。 为什么?成员函数中的 const 限定符是否意味着 class 的任何成员都不会被修改?
作为解决方法,我想我可以用这种方式修改 class D2
class D2 : public D1{
private:
double noise;
std::uniform_real_distribution<> u;
double random_correction() const override {
return u(rng); //error always here
};
public:
D2(const double x0, const double y0, const double na):
D1(x0,y0), noise(na) {u.param(std::uniform_real_distribution<>::param_type{-noise, noise});}
D2(const D2& other)=default;
D2& operator=(const D2& other)=default;
D2(D2&& other) noexcept = default;
D2& operator=(D2&& other) noexcept = default ;
~D2() override=default;
};
但是现在我收到错误:“没有匹配函数来调用 'const std::uniform_real_distribution<>' 类型的对象”。我想是因为生成一个随机数会改变 u 的状态(也可能是 rng 的状态)。这个对吗?会员有分配方便吗?
P.S。如果有任何设计错误或效率低下,请指出给我。
生成随机数不会改变 u
的状态。它的状态就是要生成的值的范围。
但它确实改变了 rng
的状态。 Pseudo-random 数字生成器通常是确定性的,这意味着 rng()
由当前状态决定,并且状态会在评估时发生变化,因此下次将生成不同的值。
要解决编译问题,您可以删除函数的 const 声明,也可以将 rng
声明为 mutable
。它仍然会遇到某些问题,例如“并发访问 class 实例会导致数据竞争”。所以你可以考虑重新考虑设计。
RNG 引擎可以从 thread_local
关键字中获益...但是您需要以某种方式设计它,这样它就不会导致可能有点棘手的性能问题。
编辑:在使用 cppreference 签入后,显然 uniform_distribution
可以包含除分布范围之外的额外信息。可能是 RNG 引擎生成的一些数据,因此它可以将它们用于将来的调用,并且不需要经常使用引擎。