【问题标题】:Can a std::map contain a reference to a constructor?std::map 可以包含对构造函数的引用吗?
【发布时间】:2015-09-09 20:29:21
【问题描述】:

有没有办法从 std::map 指向构造函数?我想使用我希望在#if 0 中使用的代码执行以下操作,但我似乎无法让它工作:

#include <map>
#include <functional>

using namespace std;

class Base { };
class A : public Base { };
class B : public Base { };

enum class Type { A, B, };

#if 0
using type_map_t = std::map<Type, std::function<Base*()>>;
type_map_t type_map = {
    {Type::A, &A::A},
    {Type::B, &B::B},
};
#endif

Base*
getBase(Type t)
{
#if 0
    auto constructor = type_map[t];
    return constructor();
#else
    switch(t)
    {
        case Type::A:
            return new A();
        case Type::B:
            return new B();
    }
#endif
}

int
main(int argc, char *argv[])
{
    Base *base = getBase(Type::A);
    return 0;
}

与其在 getBase 中使用 switch 语句,我宁愿让地图指示每种类型调用的构造函数。

std::function 想到了如何做到这一点,但似乎不可能在 C++ 中获取构造函数的地址。有没有一种优雅的方式来完成我想要在这里做的事情?

【问题讨论】:

标签: c++ c++11 stdmap std-function


【解决方案1】:

您不能根据 C++ 标准获取构造函数的地址 (C++98/03 的第 12.1.12 节和 C++11 的第 12.1.10 节):

构造函数 - 构造函数的地址不应被占用。

对于这个问题,典型的解决方案是创建特定的工厂/方法来创建对象。

【讨论】:

    【解决方案2】:

    虽然无法直接获取指向 ctor 的指针,但您可以创建自己的 ctor 对象:

    template<class Sig>
    struct ctor_t;
    
    template<class T, class...Args>
    struct ctor_t<T(Args...)> {
      T* operator()(Args...args)const {
        return new T(std::forward<Args>(args)...);
      }
      T* operator()(void*p, Args...args)const {
        return new(p) T(std::forward<Args>(args)...);
      }
      using simple = T*(*)(Args...args);
      using placement = T*(*)(void*, Args...args);
      operator simple()const {
        return [](Args...args)->T* {
          return ctor_t{}(std::forward<Args>(args)...);
        };
      }
      operator placement()const {
        return [](void*p, Args...args)->T* {
          return ctor_t{}(p, std::forward<Args>(args)...);
        };
      }
    };
    
    template<class Sig>
    static const ctor_t<Sig> ctor = {};
    

    它可以让您创建一个像 ctor 一样的对象,并且也可以转换为函数指针。

    live example.

    上面使用了一些 C++14。将ctor 替换为:

    template<class Sig>
    constexpr ctor_t<Sig> ctor() { return {}; }
    

    以及它在 C++11 中从 ctor&lt;A()&gt;ctor&lt;A()&gt;() 的使用(没有变量模板)。

    【讨论】:

    • 我很欣赏这个有启发性的答案,但我会接受 TonyB 的 lambda 答案,因为它相对简单。
    【解决方案3】:

    几点:

    1. 构造函数是一段代码,它获取原始内存并将其转换为对象。这意味着您必须拥有恰到好处的可用内存量。

    2. 没有像普通函数那样访问构造函数的机制,因为:构造函数的this指针在构造开始时就已经知道了,没有办法传入一个。 绕过这些限制的方法是使用operator new。通常的operator new 将分配对象所需的内存并在分配成功时应用构造函数代码。 (或者,有一个“placement new”允许程序员为对象提供一个指向“足够内存”的指针。它用于“emplaced”构造,你已经预先分配了一个合适的缓冲区。通常这些东西仅存在于容器库中。)

    因此,您在地图中输入的内容将不得不在函数中使用new。通常的new 可以,因为您没有任何机制来传递原始内存。

    使用 lambda,您的地图可能如下所示:

    type_map_t type_map = {
        {Type::A, []() -> Base* { return new A; } },
        {Type::B, []() -> Base* { return new B; } },
    };
    

    []() -&gt; Base* 构造表示以下代码块被视为不带参数的函数体(()-list 中没有任何内容),并且周围范围内没有任何内容([]-list 中没有任何内容),并返回Base*。它可用作std::function&lt;Base*()&gt; holder 对象的初始化值。

    您也可以直接调用您的地图条目:

    Base* base = type_map[Type::A]();
    

    【讨论】:

      猜你喜欢
      • 2014-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多