从基类指针集合调用非虚成员函数

Call a non-virtual member function from a collection of base-class pointers?

本文关键字:成员 函数 调用 基类 指针 集合      更新时间:2023-10-16

我有一个类D,我想有类A,B,C继承。但是,我想声明为pure virtual的函数是模板化的。

不幸的是,用visual studio的话来说:

member function templates cannot be virtual

A,B,C具有以完全相同的方式调用的成员操作符,尽管可能返回不同的值(即doubleunsigned int)。但我很高兴能让它只工作double):

template<typename T>
double operator()(T&, unsigned int b){/*code*/};

我怎么能正确地创建类A,B,C的多态集合(类似于std::vector<D*>,将工作,如果我不想要成员函数模板),我想做什么?

编辑:

一个我希望能够做到的例子:

std::default_random_engine rng((unsigned int) std::time(0));
std::vector<D*> v;
v.push_back(new A(0.3));
v.push_back(new B(1.0,3.2));
v.push_back(new C);
for(auto x : v){
    for(auto y : x->operator()(rng,5){
        std::cout << y << ',';
    }
    std::cout << std::endl;
}

我不完全确定您想要做什么,但是如果您将模板定义移动到类而不是方法中,那么一切都很好。这样能达到你的目的吗?

template<typename T>
class A
{
public :
    virtual double operator() (T& t, unsigned int b) = 0;
};
template<typename T>
class B : public A<T>
{
public:
    virtual double operator() (T& t, unsigned int b)
    {
         // code
    }
};
编辑:

或者,假设您不想在类级别上使用模板,那么将随机计算从多态方法中移出,然后使用简单的多态方法来处理实际的困难部分如何?这假设您只想生成一个随机数,如果您想要更多,您总是可以创建一个随机数向量,其大小在构造中确定。无论如何,下面的代码演示了我所说的:

class D
{
public :
    template<typename T>
    double operator() (T& t, unsigned int b)
    {
        double calc_rand = t();
        return DoStuff(calc_rand, b);
    }
protected :
    virtual double DoStuff(double rnd_value, unsigned int b) = 0;
};
class A : public D
{
protected :
    virtual double DoStuff(double rnd_value, unsigned int b)
    {
        return rnd_value * b;
    }
};
int main(void)
{
    std::random_device rd;
    A a;
    std::cout << a(rd, 5) << std::endl;
}

这里很可能需要使用委托。如果所有的类都有相同的名称和参数,就像这样简单:

template <typename T>
class baseWrapper{
    double method(T& a, unsigned int b) = 0;
};
template <typename T, typename myClass>
class wrapper: public baseWrapper<T>{
   myClass &instance;
   double method(T& a, unsigned int b){
      return instance.method<T>(a,b);
   };
   wrapper(myClass &instance){this->instance = instance;};
};

然后你可以创建一个委托集合:

std::vector<baseWrapper<int>*> vec;
A myObject1, B myObject2;    
wrapper<int,A> wrapper1(myObject1);
wrapper<int,B> wrapper2(myObject2);
vec.push_back(&wrapper1);
vec.push_back(&wrapper2);

如果函数的命名不同,则需要传递一个函数指针作为附加参数,或者使用SFINAE进行测试。

也许可以为成员函数的模板形参实现类型擦除。使用你的RNG示例:

class RandUIntIfc {
public:
    virtual ~RandUIntIfc() = 0;
    virtual unsigned int min() const = 0;
    virtual unsigned int max() const = 0;
    virtual unsigned int operator()() = 0;
};
class RandUInt {
public:
    template <typename RNG>
    explicit RandUInt(RNG& rng);
    unsigned int min() const;
    unsigned int max() const;
    unsigned int operator()();
private:
    std::shared_ptr<RandUIntIfc> impl_;
    template <typename RNG>
    class Impl : public RandUIntIfc {
    public:
        explicit Impl(RNG& rng);
        virtual unsigned int min() const;
        virtual unsigned int max() const;
        virtual unsigned int operator()();
    private:
        RNG& ref_;
    };
};

我希望所有实际的成员定义都是显而易见的。只剩下D和使用它的代码的更改:

class D {
public:
    virtual ~D() = 0;
    virtual double operator()(RandUInt rng, unsigned int b) = 0;
};
std::default_random_engine rng((unsigned int) std::time(0));
RandUInt typeless_rng(rng);
std::vector<D*> v;
// ...
for(auto x : v){
    for(auto y : x->operator()(typeless_rng,5){
        std::cout << y << ',';
    }
    std::cout << std::endl;
}

你不应该使用模板函数,而应该通过虚函数将delegate传递给rng。你可以这样写:

class D
{
  virtual double operator()(std::function<int()>, int)=0;
};

然后这样命名:

std::default_random_engine rng;
std::vector<D*> v;
...
for(auto x : v)
{
  std::cout << x->operator(std::bind(rng), 5) << ',';
}