调用 returns 指向基 class 的指针的模板函数时出现问题
Issue when calling template function that returns a pointer to base class
我的目标是创建许多派生对象 类 并将它们存储在 std::map
中,其中 std::string
作为键,指向该对象的指针作为值。在流程的后面,我访问所有键和值并调用一些 virtual
在派生的 类.
中重新实现的函数
我最终遇到了不得不在模板中调用模板的情况。
Model.h
#include<Base.h>
class myClass {
template<typename T>
Base* createT() { new T; }
typedef std::map<std::string, Base*(*)()> map_type;
static map_type* getMap() {
if (!map) {
map = new map_type;
}
return map;
}
template<typename T>
void registerT(std::string& s) {
getMap()->insert(std::make_pair(s, createT<T>())); // problem is here 1 of 2
}
};
Model.cc
#include <Model.h>
#include <DerivedA.h>
registerT<DerivedA>("DerivedA"); // problem is here 2 of 2
registerT<DerivedB>("DerivedB");
// To be implemented getValue(). Eventual goal is this.
auto objA = getValue("DerivedA");
objA->init(); // virtual
objA->run(); // virtual
createT
应该创建一个对象,return 我是指针。但它不起作用,编译器抛出此错误:
error: cannot call member function ‘HB::Base* myClass::createT() [with T = HB::DerivedA]’ without object
getMap()->insert(std::make_pair(s, createT<R>()));
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
我做错了什么?
您的 map
需要指向 free-standing 函数的指针,但是您的 createT()
是 non-static class 方法,因此它不兼容(另外,你的 createT()
实际上 return
什么都不是。
当 insert()
进入您的 map
时,您正在 调用 createT()
然后尝试插入其返回的对象指针,当您应该插入 createT()
本身的地址,例如:
Model.h
#include <Base.h>
#include <memory>
class myClass {
template<typename T>
static std::unique_ptr<Base> createT() { return std::make_unique<T>(); }
typedef std::map<std::string, std::unique_ptr<Base>(*)()> map_type;
static map_type& getMap() {
static map_type instance;
return instance;
}
template<typename T>
static void registerT(const std::string& s) {
getMap().emplace(s, &myClass::createT<T>);
}
static auto getValue(const std::string& s) {
return getMap()[s];
}
};
Model.cc
#include <Model.h>
#include <DerivedA.h>
myClass::registerT<DerivedA>("DerivedA");
myClass::registerT<DerivedB>("DerivedB");
auto createA = myClass::getValue("DerivedA");
auto objA = createA();
objA->init(); // virtual
objA->run(); // virtual
但是,您在描述中说要在 map
中存储 对象指针 ,而不是 函数指针 .您的代码与您的描述不符。在这种情况下,您需要更像这样的东西:
Model.h
#include <Base.h>
#include <memory>
class myClass {
template<typename T>
static std::unique_ptr<Base> createT() { return std::make_unique<T>(); }
typedef std::map<std::string, std::unique_ptr<Base>> map_type;
static map_type& getMap() {
static map_type instance;
return instance;
}
template<typename T>
static void registerT(const std::string& s) {
getMap().emplace(s, createT<T>());
}
static auto& getValue(const std::string& s) {
return getMap()[s];
}
};
Model.cc
#include <Model.h>
#include <DerivedA.h>
myClass::registerT<DerivedA>("DerivedA");
myClass::registerT<DerivedB>("DerivedB");
auto &objA = myClass::getValue("DerivedA");
objA->init(); // virtual
objA->run(); // virtual
createT<T>()
是函数的调用;你不要在这里使用函数指针。
代码中还有其他一些不理想的地方:
- 引用应该优先于指针。您可以通过
new
运算符轻松地用魔术静态替换地图的创建。 (无论如何,您都缺少名为 map
的变量的声明。)
- Return使用指针会使所有权不明确。 Return
std::unique_ptr<Base>
而不是 Base*
,您将更容易避免资源泄漏。
这是使用免费函数的代码,您可以使用所需的代码。
struct Base
{
virtual ~Base() = default;
virtual void init() = 0;
virtual void run() = 0;
};
struct DerivedA : Base
{
void init() override
{
std::cout << "DerivedA::init()\n";
}
void run() override
{
std::cout << "DerivedA::run()\n";
}
};
struct DerivedB : Base
{
void init() override
{
std::cout << "DerivedB::init()\n";
}
void run() override
{
std::cout << "DerivedB::run()\n";
}
};
template<class T>
std::unique_ptr<Base> Create()
{
return std::make_unique<T>();
}
template<class T>
void registerT(std::string&& key);
std::unique_ptr<Base> getValue(std::string const&);
/**
* Type used for restricting access to the Registrar data
*/
class RegistrarHolder
{
static std::map<std::string, std::unique_ptr<Base>(*)()>& Registrar()
{
static std::map<std::string, std::unique_ptr<Base>(*)()> registrar;
return registrar;
}
template<class T>
friend void registerT(std::string&& key);
friend std::unique_ptr<Base> getValue(std::string const&);
};
template<class T>
void registerT(std::string&& key)
{
RegistrarHolder::Registrar().emplace(std::move(key), &Create<T>);
}
std::unique_ptr<Base> getValue(std::string const& key)
{
return (RegistrarHolder::Registrar().at(key))();
}
int main() {
registerT<DerivedA>("DerivedA");
registerT<DerivedB>("DerivedB");
auto objA = getValue("DerivedA");
objA->init();
objA->run();
auto objB = getValue("DerivedB");
objB->init();
objB->run();
return 0;
}
请注意,此实现假定您希望在每次调用 getValue
时创建一个新对象,即使您之前已将相同的参数传递给函数。
我的目标是创建许多派生对象 类 并将它们存储在 std::map
中,其中 std::string
作为键,指向该对象的指针作为值。在流程的后面,我访问所有键和值并调用一些 virtual
在派生的 类.
我最终遇到了不得不在模板中调用模板的情况。
Model.h
#include<Base.h>
class myClass {
template<typename T>
Base* createT() { new T; }
typedef std::map<std::string, Base*(*)()> map_type;
static map_type* getMap() {
if (!map) {
map = new map_type;
}
return map;
}
template<typename T>
void registerT(std::string& s) {
getMap()->insert(std::make_pair(s, createT<T>())); // problem is here 1 of 2
}
};
Model.cc
#include <Model.h>
#include <DerivedA.h>
registerT<DerivedA>("DerivedA"); // problem is here 2 of 2
registerT<DerivedB>("DerivedB");
// To be implemented getValue(). Eventual goal is this.
auto objA = getValue("DerivedA");
objA->init(); // virtual
objA->run(); // virtual
createT
应该创建一个对象,return 我是指针。但它不起作用,编译器抛出此错误:
error: cannot call member function ‘HB::Base* myClass::createT() [with T = HB::DerivedA]’ without object
getMap()->insert(std::make_pair(s, createT<R>()));
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
我做错了什么?
您的 map
需要指向 free-standing 函数的指针,但是您的 createT()
是 non-static class 方法,因此它不兼容(另外,你的 createT()
实际上 return
什么都不是。
当 insert()
进入您的 map
时,您正在 调用 createT()
然后尝试插入其返回的对象指针,当您应该插入 createT()
本身的地址,例如:
Model.h
#include <Base.h>
#include <memory>
class myClass {
template<typename T>
static std::unique_ptr<Base> createT() { return std::make_unique<T>(); }
typedef std::map<std::string, std::unique_ptr<Base>(*)()> map_type;
static map_type& getMap() {
static map_type instance;
return instance;
}
template<typename T>
static void registerT(const std::string& s) {
getMap().emplace(s, &myClass::createT<T>);
}
static auto getValue(const std::string& s) {
return getMap()[s];
}
};
Model.cc
#include <Model.h>
#include <DerivedA.h>
myClass::registerT<DerivedA>("DerivedA");
myClass::registerT<DerivedB>("DerivedB");
auto createA = myClass::getValue("DerivedA");
auto objA = createA();
objA->init(); // virtual
objA->run(); // virtual
但是,您在描述中说要在 map
中存储 对象指针 ,而不是 函数指针 .您的代码与您的描述不符。在这种情况下,您需要更像这样的东西:
Model.h
#include <Base.h>
#include <memory>
class myClass {
template<typename T>
static std::unique_ptr<Base> createT() { return std::make_unique<T>(); }
typedef std::map<std::string, std::unique_ptr<Base>> map_type;
static map_type& getMap() {
static map_type instance;
return instance;
}
template<typename T>
static void registerT(const std::string& s) {
getMap().emplace(s, createT<T>());
}
static auto& getValue(const std::string& s) {
return getMap()[s];
}
};
Model.cc
#include <Model.h>
#include <DerivedA.h>
myClass::registerT<DerivedA>("DerivedA");
myClass::registerT<DerivedB>("DerivedB");
auto &objA = myClass::getValue("DerivedA");
objA->init(); // virtual
objA->run(); // virtual
createT<T>()
是函数的调用;你不要在这里使用函数指针。
代码中还有其他一些不理想的地方:
- 引用应该优先于指针。您可以通过
new
运算符轻松地用魔术静态替换地图的创建。 (无论如何,您都缺少名为map
的变量的声明。) - Return使用指针会使所有权不明确。 Return
std::unique_ptr<Base>
而不是Base*
,您将更容易避免资源泄漏。
这是使用免费函数的代码,您可以使用所需的代码。
struct Base
{
virtual ~Base() = default;
virtual void init() = 0;
virtual void run() = 0;
};
struct DerivedA : Base
{
void init() override
{
std::cout << "DerivedA::init()\n";
}
void run() override
{
std::cout << "DerivedA::run()\n";
}
};
struct DerivedB : Base
{
void init() override
{
std::cout << "DerivedB::init()\n";
}
void run() override
{
std::cout << "DerivedB::run()\n";
}
};
template<class T>
std::unique_ptr<Base> Create()
{
return std::make_unique<T>();
}
template<class T>
void registerT(std::string&& key);
std::unique_ptr<Base> getValue(std::string const&);
/**
* Type used for restricting access to the Registrar data
*/
class RegistrarHolder
{
static std::map<std::string, std::unique_ptr<Base>(*)()>& Registrar()
{
static std::map<std::string, std::unique_ptr<Base>(*)()> registrar;
return registrar;
}
template<class T>
friend void registerT(std::string&& key);
friend std::unique_ptr<Base> getValue(std::string const&);
};
template<class T>
void registerT(std::string&& key)
{
RegistrarHolder::Registrar().emplace(std::move(key), &Create<T>);
}
std::unique_ptr<Base> getValue(std::string const& key)
{
return (RegistrarHolder::Registrar().at(key))();
}
int main() {
registerT<DerivedA>("DerivedA");
registerT<DerivedB>("DerivedB");
auto objA = getValue("DerivedA");
objA->init();
objA->run();
auto objB = getValue("DerivedB");
objB->init();
objB->run();
return 0;
}
请注意,此实现假定您希望在每次调用 getValue
时创建一个新对象,即使您之前已将相同的参数传递给函数。