Search code examples
c++templatesmemberdefault-template-argument

Non-template class with a template class as a field without specifying template parameters


Is there a way to have a template class such as this

template<bool state = true>
class A
{
};

and have another class which can accept both A<true> and A<false> as a argument or field, without being a template class itself. Like this:

class B
{
public:
    B(A& arg_a)
       : a(arg_a)
    {}

private:
    A& a;
};

int main()
{
    A<true> aTrue;
    A<false> aFalse;
    B bTrue(aTrue);
    B bFalse(aFalse);
};

Or is this simply impossible because two objects of the same class with different template arguments are treated as different types by the compiler? Other suggestions on how to design this would also be appreciated. I know this design is possible if I just make the template parameter a class field, but I'd like to know if this can be done using templates parameters.


Solution

  • Or is this simply impossible because two objects of the same class with different template arguments are treated as different types by the compiler?

    The two different specializations A<true> and A<false> of the class template A are indeed different types.

    You could either overload the constructors of B to allow each of them:

    struct B {
        B(const A<true>&);
        B(const A<false>&);
    }
    

    or you could leverage polymorphism with any specialization of A deriving from a common base class:

    #include <ios>
    #include <iostream>
    
    struct Base {
        virtual bool getState() const = 0;
    };
    
    template<bool state = true>
    struct A : public Base {
        bool getState() const override { return state; }
    };
    
    struct B {
        bool state;
        B(const Base& base) : state(base.getState()) {}
    };
    
    int main() {
        A<true> a1{};
        A<false> a2{};
        std::cout << std::boolalpha 
            << B(a1).state << " "  // true
            << B(a2).state;        // false
    }
    

    Another alternative, as mentioned by @Jarod42 in the comments, is to use std::variant, given that you are using C++17 (or later); particularly, std::variant<A<true>, A<false>>.