0

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! :)

16
  • 1
    Related/dupe: Is it possible to have a constructor template with no parameters? In particular, this answer: "There is no way to explicitly specify the template arguments when calling a constructor template, so they have to be deduced through argument deduction." Commented Jul 24, 2024 at 17:49
  • template <typename T> Base() is not a useable constructor. You cannot call a constructor so there is no way to provide what T is. Commented Jul 24, 2024 at 17:50
  • The base class of the derived class shall be a template class, but you have defined a templated constructor (where the template shall be used for constructor arguments only). Commented Jul 24, 2024 at 17:52
  • @S.Gleissner on the other hand, templating the base class itself would make it more difficult, if not impossible, to have multiple derived classes with a common base class, depending on how they are used. Commented Jul 24, 2024 at 17:54
  • 1
    @BoP: std::type_identity<T> is even better. Commented Jul 24, 2024 at 18:28

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

5 Comments

C++ comes with a built-in tag type for this purpose. in_place_type<T>. So you write template<typename T> Base(std::in_place_type_t<T>) { ... use T somhow ... } and invoke it as Base(std::in_place_type<Derived>) {}
Thank you! Exactly what I was looking for. And even a standard library feature as @RaymondChen suggested. Thanks :).
And you are right. The parameter would remain unused if deduced just like that.
@RaymondChen your edit didnt compile godbolt.org/z/reWed3bhd
Sorry. Fixed. Forgot to delete the trail parens.
-1

There is no use-case for what you are trying to do. You would either move the template to the class itself or remove it since it can have no affect on construction unless you pass-in a value/instance/et. al. of T.

Using an imaginary syntax we could envision:

auto bi = <int>B();
auto bf = <float>B();
auto bs = <std::string>B();

However they would all invoke the exact same ctor so there's no point.

So there's no reason for it to be in the language even if we could add it.

2 Comments

Thanks for your answer but I certainly do have a use case for this. In the constructor I am invoking a templated member method to initialize my (as already stated in the comments below the question) behavior system with the default behaviors. Each of these need their owner type (the derived class) in order to be added which I have to provide inside the constructor. Hope it makes sense to you now :).
The constructor can very well do somehting different depending on a template parameter, and it can do so on a non-template class. godbolt.org/z/sv949rahz

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.