I like to create a registry of templated classes adressed through their polymorphic anonymous base-class. In order to construct these classes I need to store the type of the used template class in the base-class. I woulod like to make the design slim and pass the template class in the constructor of the baseclass through a custom templated constructor.
However my code fails, and i cannot figure out what the correct formulation would be. Any help appriciated:
#include <vector>
#include <string>
#include <typeinfo>
#include <memory>
class Store_Base {
const std::string stored_type_name_;
protected: //ctor
template<class Tstored>
Store_Base() : stored_type_name_(typeid(Tstored).name()) {};
protected: // dtor
virtual ~Store_Base() = default;
public:
std::string get_type_name() const {return stored_type_name_;}
};
template <class Tstored>
class Store : public Store_Base {
const Tstored stored_;
public: //ctor
Store(const Tstored stored)
: Store_Base<Tstored>(), stored_(stored) {}; // !!! compile-error
public:
Tstored get_stored() const {return stored_;}
};
int main() {
std::vector<std::unique_ptr<Store_Base>> my_vec;
my_vec.emplace_back(std::make_unique< Store<int> >(42));
my_vec.at(0)->get_type_name(); // >>> i
((Store<int>*)(my_vec.at(0).get()))->get_stored(); // >>> 42
return 0;
}
CodePudding user response:
This
Store(const Tstored stored)
: Store_Base<Tstored>()
is wrong, because it attempts to initialize the base class Store_Base<Tstored>
, but Store_Base
is not a template. The base class to be initialized is Store_Base
not Store_Base<Tstored>
.
The way to call a templated constructor is to have the template argument deduced. See here C template constructor.
You can use a tag to enable deduction:
#include <vector>
#include <string>
#include <typeinfo>
#include <memory>
template <typename T>
class Tag{};
class Store_Base {
const std::string stored_type_name_;
protected: //ctor
template<class Tstored>
Store_Base(Tag<Tstored>) : stored_type_name_(typeid(Tstored).name()) {};
protected: // dtor
virtual ~Store_Base() = default;
public:
std::string get_type_name() const {return stored_type_name_;}
};
template <class Tstored>
class Store : public Store_Base {
const Tstored stored_;
public: //ctor
Store(const Tstored stored)
: Store_Base(Tag<Tstored>{}), stored_(stored) {}; // !!! compile-error
public:
Tstored get_stored() const {return stored_;}
};
int main() {
std::vector<std::unique_ptr<Store_Base>> my_vec;
my_vec.emplace_back(std::make_unique< Store<int> >(42));
my_vec.at(0)->get_type_name(); // >>> i
((Store<int>*)(my_vec.at(0).get()))->get_stored(); // >>> 42
return 0;
}
Note that I didn't change more than necessary and that this code still fails to compile due to the destructor of Store_Base
being protected, ie std::unique_ptr
cannot access it.