【问题标题】:Call templated member function vs. templated global function to create object in generic factory调用模板化成员函数与模板化全局函数在泛型工厂中创建对象
【发布时间】:2012-05-17 06:26:36
【问题描述】:

在网上搜索了工厂模式的不同方法后,我实现了自己的版本,对此我非常满意。 Register 成员函数为模板类型创建一个函数指针,使用索引作为键将其存储在 std::map 中。下面的代码可以轻松编译和运行(Windows 7 64 位,Code::Blocks 10.05 with GCC)。

#ifndef OBJECT_FACTORY_HPP
#define OBJECT_FACTORY_HPP

#include <map>

namespace internal {
    template<typename BaseType, typename ObjectType>
    BaseType* CreateFunction() {
        return new ObjectType;
    }
}

template<typename BaseType, typename IndexType>
class ObjectFactory {
    public:
        ObjectFactory();

        template<typename ObjectType>
        bool Register(const IndexType& index);

        bool Unregister(const IndexType& index);

        BaseType* Create(const IndexType& index);

    private:
        typedef BaseType* (*creationCallback)();
        typedef std::map<IndexType, creationCallback> Registry;
        Registry registry;

//    private:
//        template<typename ObjectType>
//        BaseType* CreateFunction();
};

template<typename BaseType, typename IndexType>
ObjectFactory<BaseType, IndexType>::ObjectFactory() {
    registry.clear();
}

template<typename BaseType, typename IndexType>
template<typename ObjectType>
bool ObjectFactory<BaseType, IndexType>::Register(const IndexType& index) {
    if (registry.find(index) != registry.end())
        return false;

    registry[index] = &internal::CreateFunction<BaseType, ObjectType>;
    // registry[index] = &CreateFunction<ObjectType>; <-- FAILS!
    return true;
}

template<typename BaseType, typename IndexType>
bool ObjectFactory<BaseType, IndexType>::Unregister(const IndexType& type) {
    if (registry.find(type) == registry.end())
        return false;

    return (registry.erase(type) == 1);
}

template<typename BaseType, typename IndexType>
BaseType* ObjectFactory<BaseType, IndexType>::Create(const IndexType& index) {
    if (registry.find(index) == registry.end())
        return NULL;

    return registry[index]();
}

//template<typename BaseType, typename IndexType>
//template<typename ObjectType>
//BaseType* ObjectFactory<BaseType, IndexType>::CreateFunction() {
//    return new ObjectType();
//}

#endif

我最初的方法是让 CreateFunction 作为私有成员以对用户隐藏它(查看注释部分,注意非成员函数需要一个额外的模板参数)。但是,这会失败并显示以下错误消息,所有这些都指向我在 Register 成员函数中存储函数指针的行:

In member function 'bool ObjectFactory<BaseType, IndexType>::Register(const IndexType&) [with ObjectType = Triangle, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]':|
instantiated from here
error: no matches converting function 'CreateFunction' to type 'class Shape* (*)()'|
error: candidates are: template<class ObjectType> BaseType* ObjectFactory::CreateFunction() [with ObjectType = ObjectType, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]|

我写了一个小测试客户端进行测试:

#include "ObjectFactory.hpp"
#include <iostream>

class Shape {
    public:
        Shape() {}
        virtual void print() { std::cout << "At heart, I'm a shape"; }
};

class Triangle : public Shape {
    public:
        Triangle() {}
        void print() { Shape::print(); std::cout << ", but I'm truly a triangle" << std::endl; }
};

int main(int argc, char* argv[]) {
    ObjectFactory<Shape, std::string> objectFactory;

    objectFactory.Register<Triangle>("triangle");

    Shape* triangle = objectFactory.Create("triangle");
    triangle->print();

    delete triangle;

    return 0;
}

这甚至可能吗?我觉得应该是,而且我知道我以某种方式调用了不正确的成员函数,但我不明白为什么。在相关说明中,由于有人可能会提到它,我计划使用 *boost::shared_ptr* 进行分配,而不是普通的 new 运算符;)也欢迎任何其他关于实施的建议或建议。

【问题讨论】:

  • BaseType* CreateFunction() { return new ObjectType; } 应该是 std::unique_ptr&lt;BaseType&gt; CreateFunction() { std::unique_ptr&lt;BaseType&gt; p(new ObjectType()); return p; }。对delete说“不”就行了。
  • 如果您阅读了我帖子的最后一段,您会看到我已经计划使用 boost::shared_ptr。我已经有相当多的内存泄漏,所以我同意你的最后陈述。还提醒我应该阅读 std::unique_ptr,所以谢谢 :)

标签: c++ function templates pointers


【解决方案1】:

在我们的文件中,我们匿名我们的命名空间:

// namespace internal {  
   namespace          {
    template<typename BaseType, typename ObjectType> 
    BaseType* CreateFunction() { ... }  
}

现在可以按照最初编写的方式调用非静态函数,而无需命名空间限定:

// registry[index] = &internal::CreateFunction<ObjectType>;  
   registry[index] =           &CreateFunction<ObjectType>; 

文件范围的CreateFunction 函数对翻译单元之外的代码是不可见的,并且只能由类ObjectFactory 调用。

这很接近问题中提出的CreateFunction(来自ObjectFactory)的private 访问说明符。

【讨论】:

  • 这也是一个非常好的和稍微干净的方法(虽然你忘记了第一个模板参数)。谢谢你的建议:)
【解决方案2】:

创建函数应该是静态成员:

template<typename ObjectType> static BaseType* CreateFunction();

(上面的代码,没有static,在你的例子中被注释掉了)。

现在您的其他注释掉的代码是有效的:

registry[index] = &CreateFunction<ObjectType>;

编辑:澄清一些混乱:

这里的问题主要在于语法。模板和private在这里并不重要,让我们简化一下情况:

struct ObjectFactory {
    void CreateFunction() {std::cout << "message";}
    void AnotherFunction()
    {
        ... = &CreateFunction; // what's that?
    }
};

如果CreateFunction是一个非静态成员函数,则&amp;CreateFunction的语法不正确;我想 gcc 就是在抱怨这一点。然而,更糟糕的是,MS Visual Studio 试图通过在内部将语法“更正”为&amp;ObjectFactory::CreateFunction 并尝试使用它来“帮助”您;但它会因无法理解的错误消息而失败(您拥有模板这一事实会使它们更加严重)。

我不确定 Visual Studio 部分(我记得几年前使用 MSVC 6 时遇到过这样的问题;较新版本的 Visual Studio 没有这个问题)。

【讨论】:

  • 代码已被注释掉,因为它是原始方法。新的使用在内部命名空间中声明的全局函数。
  • @NordCoder 我将您的问题理解为“我的注释掉的代码不起作用;我该如何解决?”
  • 抱歉,我会尝试重新表述我的问题。我刚刚尝试过,它完美地工作,非常感谢:) 我不明白为什么这应该是无效的语法(即使 GCC 告诉我它是)或者为什么私有成员函数不应该工作。也许 GCC 和 MS Visual Studio 做同样的事情?将函数指针 typedef 声明为 typedef BaseType*(ObjectFactory::*creationCallback)();反而会产生更复杂的错误。
  • 为垃圾邮件道歉,但这确实是我的问题,我只是选择注释掉一种方法以避免重复代码以显示两种方法。不过可能会更清楚。
  • 对。我想我跟着,谢谢你的澄清。那么正确的语法是什么?再想一想,我想我也许可以使用 boost::function: typedef std::map&lt;IndexType, boost::function&lt;boost::shared_ptr&lt;BaseType&gt; (ObjectFactory*, void)&gt;&gt; Registry; Registry registry; // ... registry[index] = &amp;ObjectFactory::CreateFunction&lt;ObjectType&gt;; // ... return registry[index](*this, void); 不确定这是否可行(我目前还不是 Boost 专家)...请注意,我也在使用boost::shared_ptr 在这里(我设法让它同时工作)。
猜你喜欢
  • 1970-01-01
  • 2012-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多