首页 > 解决方案 > Choose type of class members at run time

问题描述

I am trying to build a modular class containing other classes as members, where the user can specify the type of these classes at run time. I tried to implement this with templates, but realized that this is probably not the right approach. Here is a minimal working example:

#include <iostream>

// One of the modules. Code will consist of multiple of these modules.
template <class T, class U>
class Module1 {
public:
    T subModule1;
    U subModule2;
};

class Add {
public:
    double Apply(double a) {
        return a + x;
    }

private:
    double x = 2.5;
};

class Multiply {
public:
    double Apply(double a) {
        return a * pi;
    }

private:
    double pi = 3.14;
};

class Divide {
public:
    double Apply(double a) {
        return a / pi;
    }

private:
    double pi = 3.14;
};

int main(int argc, char* argv[])
{
    // User input to determine the used submodules
    // Here the user decides to use the Add and Multiply submodules.
    Module1<Add, Multiply> module1;

    std::cout << module1.subModule1.Apply(3) + module1.subModule2.Apply(2) << std::endl;
}

But how could I implement the decision which of the submodules should be used? E.g., if the user wants to use Divide and Add I have to create Module1 as

Module1<Divide, Add> module1;

There will be many different submodules, so branching the code with if's is also not be possible. I'm beginning to doubt this is possible with templates. Do you know a better way to accomplish this? Note that the implementation should also be very performant.

标签: c++classtemplatesmetaprogramming

解决方案


Templates are used for compile-time polymorphism, while you want run-time polymorphism.

If you have a closed set of possible submodules, you can use std::variant. It basically is a type-safe union:

using SubmoduleVariant = std::variant<Add, Subtract, Multiply, Divide>;

class Module1 {
public:
    SubmoduleVariant subModule1;
    SubmoduleVariant subModule2;
};

// ...

Module1 m;

     if(userInput == 0) { m.subModule1 = Add{}; }
else if(userInput == 1) { m.subModule1 = Subtract{}; }
else if(userInput == 2) { m.subModule1 = Multiply{}; }
else if(userInput == 3) { m.subModule1 = Divide{}; }

If you don't know the set of possible types in advance, but they all conform to the same interface, you can use virtual functions and a base class.


If you don't know the set of possible types in advance, and they do not conform to the same interface, you can use std::any, which is basically a wrapper over "an object of any type".


推荐阅读