调用 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 时创建一个新对象,即使您之前已将相同的参数传递给函数。