Search code examples
c++templatestypespolymorphismwrapper

class A with member B that has a constructor argument with a variable type


I'm looking to make class A a generic UI Inputbox, that holds a member B that gets a constructor argument with variable type. Class A should then be able to adapt the amount of input boxes depending on the type of the constructor argument of class B.

template<typename coordinatesType>
class B{
    B(T coordinates); //constructor defined in cpp
    T coordinates; //coordinates of different input type
};

class A{
    A(); //in constructor, define the type of UI input boxes, depending on type of B
    std::vector<B<T>> vector; //should hold a vector with class B without making class A a template itself
};

Now class A has to be a template class too, because appearently I can't put the member B in the header of class A without giving class A a template argument too (T would be undefined). Since every element of the B vector should have different types, giving class A a template argument and thereby defining the type of each element of the B vector(?) is not an option. There is also the issue that templates objects have to be defined in compile time right?

I'm thinking of building a polymorphic wrapper around class B to avoid this problem, but i don't know wether this is even possible.


Solution

  • Using Polymorphism

    As others pointed out in the comments, you can use a non-template base class of B<T> and store smart pointers in the vector:

    #include <vector>
    #include <string>
    #include <memory>
    
    struct Base {
        virtual ~Base(){}
    };
    
    template<typename T>
    class B : public Base
    {
    public:
        B(T coords) : coordinates{coords} {}; //constructor defined in cpp
        T coordinates; //coordinates of different input type
    };
    
    class A{
    public:
        A(){}; //in constructor, define the type of UI input boxes, depending on type of B
        std::vector<std::unique_ptr<Base>> vector; //should hold a vector with class B without making class A a template itself
    };
    
    int main()
    {
        A a;
    
        a.vector.emplace_back(new B<int>(5));
        a.vector.emplace_back(new B<bool>(false));
        a.vector.emplace_back(new B<std::string>("Hello World"));
        return 0;
    }
    

    Live Code: https://godbolt.org/z/7G89Wf4qG

    Using std::variant

    If you know all variants that B can be in in advance, you could use std::variant:

    #include <vector>
    #include <string>
    #include <variant>
    
    template<typename T>
    class B
    {
    public:
        using value_type=T;
    
        B(T coords) : coordinates{coords} {}; //constructor defined in cpp
        T coordinates; //coordinates of different input type
    };
    
    using BVariant = std::variant<
     B<int>,
     B<bool>,
     B<std::string>
    >;
    
    class A{
    public:
        A() {} //in constructor, define the type of UI input boxes, depending on type of B
        std::vector<BVariant> vector; //should hold a vector with class B without making class A a template itself
    };
    
    int main()
    {
        A a;
    
        a.vector.emplace_back(B<int>(5));
        a.vector.emplace_back(B<bool>(false));
        a.vector.emplace_back(B<std::string>("Hello World"));
        return 0;
    }
    

    Live Code: https://godbolt.org/z/dWdbr9z6n