0

I've got a class D, that I want to have classes A,B,C inherit from. However, the functions that I want to declare as pure virtual are templated.

Unfortunately, in the words of visual studio:

member function templates cannot be virtual

Classes A,B,C have a member operator called in the exact same manner, though may return different values (double or unsigned int namely. But I'd be happy to get it to work with just double):

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

How could I properly create a polymorphic collection of classes A,B,C (similar to a std::vector<D*> that would work if I didn't want member function templates) that does what I'm trying to do?

EDIT:

An example of what I'd like to be able to do:

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;
}
druckermanly
  • 2,563
  • 12
  • 27
  • I think you might be able to achieve what you are looking for by implementing type erasure in the fashion of `boost::any`. But I am not completely sure what your objective is. Could you post the entire code you are working with---specifically, your current member function template definitions? – Derrick Turk Jun 17 '14 at 02:39
  • You can use the visitor pattern basically as is, with the usual downside (the visitor depends on all the derived classes together). – n. 'pronouns' m. Jun 17 '14 at 02:46

4 Answers4

2

I'm not entirely sure what you want to do, but if you move the template definition to the class instead of the method, everything is happy. Does that do what you want?

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
    }
};

EDIT:

Or, given that you don't want the template at the class level, what about moving the random calculation out of the polymorphic method, and then having a simple plymorphic method for the actual hard part. This assumes you only want to generate one random number, If you want more, you could always create a vector of random numbers, the size of which is determined in construction. Anyway the code below demonstrates what I am talking about:

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;
}
Simon Parker
  • 1,594
  • 14
  • 32
  • Unfortunately, no. Think of these functions as the distributions in `std::random`, that will take any random-number generator as the first input. I wouldn't want to template with the random-number generator! I suppose my poor explanation needs some elaboration. – druckermanly Jun 17 '14 at 00:14
  • I'm not sure why not (but I think I might be missing the point). If a single object is going to be used with a single T, then there is no problem. If you are planning to use different T values with the one object, a templated class obviously won't work. However, I would argue against doing that anyway, as it makes the responsibilities of the object less defined. I don't suppose you could edit your question with a code you want working? – Simon Parker Jun 17 '14 at 00:28
  • I've since edited my post. Imagine that the `operator()(RNG, unsigned int)` returns only `double` – druckermanly Jun 17 '14 at 00:44
  • I've edited my response to give you something like you are after. – Simon Parker Jun 17 '14 at 02:36
  • Thank you very much! Can't believe I didn't think of something like this earlier! If only `member function templates` could be virtual... but with how vtables are currently implemented, I suppose that's not exactly viable. But moving things from compile-time to run-time could fix that maybe? (This is my pondering in the realm of things I don't understand well enough.) Thanks again for you assistance! – druckermanly Jun 17 '14 at 02:38
0

You'll most probably need to use delegates here. If all the classes have the same name and parameters, it's as easy as doing:

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;};
};

And then you can create a collection of delegates:

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);

If the functions are named differently, you'll need to pass a function pointer as an additional parameter, or test it with SFINAE.

Community
  • 1
  • 1
Paweł Stawarz
  • 3,704
  • 2
  • 14
  • 26
  • While this seems to be the only way to achieve what I want to do, it seems to be very contrived and hard to read. An analog of what I want to do is have distributions (similar to those in `std::random`) that all inherit from a base class, that has a member `operator()(pRNG)` that returns a value corresponding to the random distribution. It seems unnecessary that I should template my collection to the type of `pRNG` being used. But there may not be another way... – druckermanly Jun 17 '14 at 00:24
  • @user2899162 you don't have to template your collection if you remove the parameter from the `operator()` function. Maybe set the parameter with a `set` function and then use it when calling `operator()`? That + delegates would result in doing `std::vector vec;`. – Paweł Stawarz Jun 17 '14 at 00:44
  • If I understand what you're saying, then the `set` function would need to be called individually (and not as part of a polymorphic collection). Which would defeat the purpose! Could you perhaps take a look at my post and see the "ideal example" I've just edited in? – druckermanly Jun 17 '14 at 00:51
  • @user2899162 in your code the `operator()` is templated, yet - in your ideal code - you don't pass the type to template it. If you would (and you actually __have to__ to make your ideal code work), then you can as well call a `set` function instead. – Paweł Stawarz Jun 17 '14 at 01:08
  • Ah, but with a `set` function I wouldn't be able to treat all of `A,B,C` polymorphically, defeating the purpose of all this! – druckermanly Jun 17 '14 at 02:11
  • Make the operator() a base class method that is templated. It can call a set method, and then call a pure virtual "DoStuff" method. That should give you what you want. – Simon Parker Jun 17 '14 at 02:15
  • Yeah, I didn't yet understand your point when I made that comment. I understand (and appreciate) what you're saying now! – druckermanly Jun 17 '14 at 03:04
0

You shouldn't use a template function but just pass delegate to the rng through the virtual function. So basically you could do this:

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

And call it like this:

std::default_random_engine rng;
std::vector<D*> v;
...
for(auto x : v)
{
  std::cout << x->operator(std::bind(rng), 5) << ',';
}
JarkkoL
  • 1,839
  • 8
  • 16
0

Perhaps you can implement a type erasure for the template parameter of the member functions. Going with your RNG example:

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_;
    };
};

I hope all the actual member definitions are obvious. That leaves just the change to D and code that uses it:

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;
}
aschepler
  • 65,919
  • 8
  • 93
  • 144