1

Similar questions were posted and answered here and here but the proposed solutions don't work for me.

I have three classes with multilevel inheritance:

class Model
{
public:
    Model();
    template <typename InputModelType>
    void importModel(const InputModelType &m);
    virtual void process(); 
};

class SpecialModel : public Model
{
public:
    SpecialModel();
    template <typename InputModelType>
    void importSpecialModel(const InputModelType &m);
    virtual void process() override; 
};

class SpecialSpecialModel : public SpecialModel
{
public:
    SpecialModel();
    template <typename InputModelType>
    void importSpecialSpecialModel(const InputModelType &m);
    virtual void process() override; 
};

The child model is a special case of the parent model and can be stored in a simpler structure and thus, can be processed faster.

What I would like to do is to instantiate the model, depending on an input parameter model_type that is specified by the user, something like this:

Model* model;
switch(model_type){
case 1:
    model = new SpecialModel;
    model->importSpecialModel(gm);
    break;
case 2:
    model = new SpecialSpecialModel;
    model->importSpecialSpecialModel(gm);
    break;
default:
    model = new Model;
    model->importModel(gm);
    break;
}

model->process();

Using the above code, I got the following errors:

‘class Model’ has no member named ‘importSpecialModel’

‘class Model’ has no member named ‘importSpecialSpecialModel’

The problem is that, the import functions are templated, therefore it is not valid to define them as virtual functions in the base class and then override in the child class.

Community
  • 1
  • 1
f10w
  • 1,524
  • 4
  • 24
  • 39

2 Answers2

5

You can only use function from the static type of the object. You may do the following which use the derived type.

std::unique_ptr<Model> CreateModel(int model_type, const InputModelType &m)
{
    switch(model_type)
    {
        case 1:
        {
            auto model = std::make_unique<PairwiseMRF>();
            model->importSpecialModel(gm);
            return model; // or std::move(model)
        }
        case 2:
        {
            auto model = std::make_unique<PairwiseMetricMRF>();
            model->importSpecialSpecialModel(gm);
            return model; // or std::move(model)
        }
        default:
        {
            auto model = std::make_unique<Model>();
            model->importModel(gm);
            return model;
        }
    }
}

And then

auto model = CreateModel(model_type, gm);
model->process();
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    @RSahu: `process()` seems not part of the creation of the model, that's why I omit it. I add the usage case to be more similar to OP's code. – Jarod42 May 12 '17 at 16:53
  • Thanks for the very quick answer! I saw it right after I submitted the question but it took me some time to test. It works! But it seems that std::make_unique is C++14 only, isn't it? I had to compile using C++14. Is there a way to make it work in C++11? – f10w May 12 '17 at 17:11
  • You may still implement your own `make_unique` or use `unique_ptr` constructor with new. – Jarod42 May 12 '17 at 17:13
  • Thanks. By the way, using this solution, the deallocation of the variables (e.g. virtual destructors) is not necessary, right? – f10w May 12 '17 at 17:17
  • With `std::unique_ptr`, base class should have virtual destructor (but you don't have to call delete manually). with `shared_ptr` , it would not be required. – Jarod42 May 12 '17 at 19:01
  • Thanks again, @Jarod42! – f10w May 16 '17 at 11:43
1

This is a variation of the answer by @Jarod42. You can avoid using a switch statement by using a map of function pointers.

// Independent functions to import the various model types

std::unique_ptr<Model> importPairwiseMRF(GmType gm)
{
   auto model = std::make_unique<PairwiseMRF>();
   model->importSpecialModel(gm);
   return model;
}

std::unique_ptr<Model> importPairwiseMetricMRF(GmType gm)
{
   auto model = std::make_unique<PairwiseMetricMRF>();
   model->importSpecialSpecialModel(gm);
   return model;
}

std::unique_ptr<Model> importModel(GmType gm)
{
   auto model = std::make_unique<Model>();
   model->importModel(gm);
   return model;
}

// Function to import a model given a model_type and the import data.
std::unique_ptr<Model> importModel(int model_type, GmType gm)
{
   // Define a function type that can take gm and return a Model*
   typedef = std::unique_ptr<Model> (*Function)(GmType gm);

   // Create a map of the functions known so far.
   std::map<int, Function> theMap =
   {
      {1, importPairwiseMRF},
      {2, importPairwiseMetricMRF},
      {3, importModel}
   };

   // If there is a function for the given model_type, use it.
   // Otherwise, return nullptr.
   if ( theMap[model_type] != nullptr )
   {
      return theMap[model_type].second(gm);
   }
   else
   {
      return {};
   }
}

And use it as:

auto model = importModel(model_type, gm);
if ( model )
{
   model->process();
}
Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270