我试图编写一个可以使用 CRTP 注册类型的工厂,就像这个问题,但我遇到了一些问题。
#include <iostream>
#include <map>
#include <functional>
class A
{
public:
virtual ~A() = default;
virtual void junk() = 0;
};
class Factory
{
public:
static A* get(const std::string& name)
{
return map()[name]();
}
template<typename T>
static bool reg()
{
map()[T::asdf] = [](){ return new T(); };
return true;
}
static std::map<std::string, std::function<A*()>>& map()
{
static std::map<std::string, std::function<A*()>> m;
return m;
}
};
template<typename T>
class AutoReg : public A
{
public:
AutoReg(){ std::ignore = registered; }
static inline bool registered = Factory::reg<T>();
};
class B : public AutoReg<B>
{
public:
//B(){}
void junk() override { std::cout << "B" << std::endl; }
static const inline std::string asdf = "asdf";
};
int main(int argc, char** argv)
{
Factory::get("asdf")->junk();
return 0;
}
当
B
的构造函数被注释掉时,上面的代码(是的,我知道它会泄漏内存)会失败,但当 B
的构造函数被取消注释时,它会工作。我的理解是 B
的默认构造函数应该调用 AutoReg<B>
的默认构造函数,并且我自己是否定义构造函数应该不重要。但由于某种原因,如果我不手动为 AutoReg<B>::registered
定义构造函数,B
永远不会被初始化(B() = default;
也失败)。为什么会这样?
根据https://en.cppreference.com/w/cpp/language/default_constructor:
如果构造函数是隐式声明的(C++11 之前),则隐式声明或显式默认的默认构造函数未定义为已删除(C++11 起),而是已定义(即生成函数体)如果 odr 使用或需要进行常量求值(自 C++11 起),则由编译器编译),并且它与具有空主体和空初始值设定项列表的用户定义构造函数具有相同的效果。
由于
B
的构造函数永远不会被 odr 使用,因此它不会被生成,因此 AutoReg<B>
的构造函数永远不会被 odr 使用,并且 AutoReg<B>::registered
永远不会被 odr 使用,因此它永远不会被初始化或向工厂注册。