I was trying to write a factory that types could register to using CRTP, like this question, and I was having some issues.
#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;
}
The code above (yes, I'm aware it leaks memory) fails when the constructor of B
is commented out, but works when the constructor of B
is uncommented. My understanding is that the default constructor of B
should call the default constructor of AutoReg<B>
and it shouldn't matter if I define the constructor myself or not. But for some reason, AutoReg<B>::registered
never gets initialized if I don't manually define a constructor for B
(B() = default;
also fails). Why is this the case?
According to https://en.cppreference.com/w/cpp/language/default_constructor:
If the constructor is implicitly-declared(until C++11)the implicitly-declared or explicitly-defaulted default constructor is not defined as deleted(since C++11), it is defined (that is, a function body is generated and compiled) by the compiler if odr-used or needed for constant evaluation(since C++11), and it has the same effect as a user-defined constructor with empty body and empty initializer list.
Since B
's constructor is never odr-used, it doesn't get generated, so AutoReg<B>
's constructor is never odr-used, and AutoReg<B>::registered
never gets odr-used, and so it never gets initialized or registered to the factory.