Search code examples
c++templatescompilationconstexprtemplate-specialization

C++ Set Template Bool Parameter from Class Constructor Argument


I've simplified the scenario as much as possible below. Imagine a class that has template specialization using a bool parameter.

template <bool hasExtraParam>
class TestClass {};

template <>
class TestClass<true> {
   public:
    int param1;
    int param2;
};

template <>
class TestClass<false> {
   public:
    int param1;
};

Now, I would like to have a container class that holds a large amount of these TestClasses as member variables. I want to be able to set the template parameter of each member variable based on the constructor arguments as below:

constexpr bool ep1, ep2, ep3, ep4;
class Container
{
   public:
    constexpr Container(bool extraParam1, bool extraParam2, bool extraParam3,
                        bool extraParam4)
    {
        ep1 = extraParam1;
        ep2 = extraParam2;
        ep3 = extraParam3;
        ep4 = extraParam4;
    }
    TestClass<ep1> testClass1;
    TestClass<ep2> testClass2;
    TestClass<ep3> testClass3;
    TestClass<ep4> testClass4;
};

How can I achieve this pattern? I want for my actual use-case to pass in a large config struct that has a boolean that will link to each member variable setting its respective template parameter. I cannot wrap my head around how to achieve this and feel like I'm missing some alternate fundamental approach to the problem. Also, I don't this its feasible for Container to have a bunch of templated arguments for scalability purposes since the config struct can be large.


Solution

  • What you are attempting is not possible in C++. Template aarguments can only be specified at compile-time. You will have to instantiate the objects dynamically at runtime instead, eg:

    class TestClassBase {
    public:
        virtual ~TestClassBase() = default;
    
        // common members of both classes...
    
        // virtual methods for both classes to override...
    };
    
    template <bool hasExtraParam>
    class TestClass {};
    
    template <>
    class TestClass<true> : public TestClassBase {
    public:
        int param1;
        int param2;
    
        // override virtual methods as needed...
    };
    
    template <>
    class TestClass<false> : public TestClassBase {
    public:
        int param1;
    
        // override virtual methods as needed...
    };
    
    class Container
    {
    public:
        Container(bool extraParam1, bool extraParam2, bool extraParam3, bool extraParam4)
        {
            testClass1 = extraParam1 ? new TestClass<true> : new TestClass<false>;
            testClass2 = extraParam2 ? new TestClass<true> : new TestClass<false>;
            testClass3 = extraParam3 ? new TestClass<true> : new TestClass<false>;
            testClass4 = extraParam4 ? new TestClass<true> : new TestClass<false>;
        }
    
        ~Container()
        {
            delete testClass1;
            delete testClass2;
            delete testClass3;
            delete testClass4;
        }
    
        TestClassBase* testClass1;
        TestClassBase* testClass2;
        TestClassBase* testClass3;
        TestClassBase* testClass4;
    };
    

    Alternatively:

    template <bool hasExtraParam>
    class TestClass {};
    
    template <>
    class TestClass<true> {
    public:
        int param1;
        int param2;
    };
    
    template <>
    class TestClass<false> {
    public:
        int param1;
    };
    
    class Container
    {
    public:
        Container(bool extraParam1, bool extraParam2, bool extraParam3, bool extraParam4)
        {
            if (extraParam1)
                testClass1.emplace<TestClass<true>>();
            else
                testClass1.emplace<TestClass<false>>();
    
            if (extraParam2)
                testClass2.emplace<TestClass<true>>();
            else
                testClass2.emplace<TestClass<false>>();
    
            if (extraParam3)
                testClass3.emplace<TestClass<true>>();
            else
                testClass3.emplace<TestClass<false>>();
    
            if (extraParam4)
                testClass4.emplace<TestClass<true>>();
            else
                testClass4.emplace<TestClass<false>>();
        }
    
        std::variant<TestClass<true>, TestClass<false>> testClass1;
        std::variant<TestClass<true>, TestClass<false>> testClass2;
        std::variant<TestClass<true>, TestClass<false>> testClass3;
        std::variant<TestClass<true>, TestClass<false>> testClass4;
    };