c++templatesabstract-class

Templated constructor of abstract class without parameter type deduction in C++


I want my abstract base class to have a templated constructor, but can't get it to work without an extra parameter to deduct the type from.

I want to do something like this:

class Base {
public:
    template <typename T>
    Base() {
        /*Do something*/
    }
    
    virtual void foo() = 0;
};

class Derived : public Base {
public:
    Derived() : Base</*Some type*/>() {} // <-- Forbidden
    
    void foo () {}
};

I know that this does not work for non abstract classes because the syntax for constructing them would have the constructor template and a possible class template specification collide* with each other, but I can't see why it wouldn't work for abstract classes since you aren't able to instantiate them on their own.

*(For example with Base<int>() (assuming base is not abstract), it is not clear what the specified template is for. Class template or constructors function template.)

Any help would be greatly appreciated! :)


Solution

  • I suppose you do not want to pass the parameter of type T that would let you deduce T because the parameter would be unused. Otherwise you could just do that.

    You can employ a tag whose only purpose is to transport the information on which T should be used and pass that as parameter:

    template <typename T> struct tag{};
    template <typename tag> struct T_from_tag;
    template <typename T> struct T_from_tag<tag<T>> { using type = T;};
    template <typename tag> using T_from_tag_t = typename T_from_tag<tag>::type;
    
    class Base {
    public:
        template <typename tag>
        Base(tag) {
            using T = T_from_tag_t<tag>;
        }
        
        virtual void foo() = 0;
    };
    
    class Derived : public Base {
    public:
        Derived() : Base(tag<Derived>{}) {}
        
        void foo () {}
    };
    

    Live Demo

    What you suggest wouldn't work for example when Base itself is a template. Base<T><U> just isn't C++ syntax. I suppose it would not be impossible to invent a language that would make what you want possible more directly, but in C++ that isn't required because there are other ways, for example using a tag as illustrated above.

    PS: As pointed out by Raymond Chen C++17 introduced a standard template to achieve the same: std::in_place_type<T>:

    #include <iostream>
    #include <utility>
    #include <type_traits>
    
    struct foo {
        template <typename T>
        foo(std::in_place_type_t<T>) {
            if constexpr (std::is_same_v<int,T>) {
                std::cout << "T is int\n";
            }
        }
    };
    
    int main() {
        foo{std::in_place_type<int>};  // prints 
        foo{std::in_place_type<double>};  // does not print
    }
    

    Live Demo