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! :)
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 () {}
};
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
}