std::map 可以包含对构造函数的引用吗?

Can a std::map contain a reference to a constructor?

有没有办法从 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++98/03 的第 12.1.12 节和 C++11 的第 12.1.10 节):

Constructors - The address of a constructor shall not be taken.

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

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

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 = {};

它可以让你创建一个像构造函数一样的对象,也可以变成一个函数指针。

live example.

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

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

及其对 C++11(无变量模板)从 ctor<A()>ctor<A()>() 的使用。

几点:

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

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

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

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

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

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

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

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