Список инициализатора C++11 + указатель на функцию вызывает головную боль
Я пытаюсь создать глобальную карту, которая будет сопоставлять строки с заводскими функциями. Это позволяет мне иметь функцию построителя, которая считывает ключ из INI-файла, а затем передает этот INI-файл с соответствующим разделом в правильную фабричную функцию. Вот код в шапке с глобальной картой:
typedef Primitive* (*factory_func_t)(const ::INI&, const std::string&);
const std::map<std::string, factory_func_t> factory_funcs = {
{ SphereFactory::ID, &SphereFactory::create_sphere },
{ QuadFactory::QUAD_ID, &QuadFactory::create_quad },
{ QuadFactory::PQUAD_ID, &QuadFactory::create_pquad }
};
И вот пример одного из этих фабричных классов:
class SphereFactory
{
public:
static const std::string ID;
static Sphere* create_sphere(const ::INI&, const std::string& section);
SphereFactory() = delete;
private:
static const std::string CENTER_KEY;
static const std::string RADIUS_KEY;
};
const std::string SphereFactory::ID = "Sphere";
const std::string SphereFactory::CENTER_KEY = "Center";
const std::string SphereFactory::RADIUS_KEY = "Radius";
Все это дает мне ошибку при компиляции:
error: could not convert `{{cg::prim::factory::SphereFactory::ID,
cg::prim::factory::SphereFactory::create_sphere},
{cg::prim::factory::QuadFactory::QUAD_ID,
cg::prim::factory::QuadFactory::create_quad},
{cg::prim::factory::QuadFactory::PQUAD_ID,
cg::prim::factory::QuadFactory::create_pquad}}'
from `<brace-enclosed initializer list>'
to `const std::map<std::basic_string<char>, cg::Primitive* (*)(const INI&,
const std::basic_string<char>&)>'
Весь приведенный выше код находится в cg::prim::factory
пространство имен, в случае, если это имеет значение. И то и другое Quad
а также Sphere
наследовать от Primitive
, я использую g++ -O3 -Wall -Wextra -pedantic -std=c++11
Скомпилировать.
Почему это не компилируется?
1 ответ
Вы пытаетесь использовать ковариантные типы возврата: typedef
для Primitive *
но заводы возвращаются Sphere *
и т. д. Вы можете включить альтернативные фабричные функции-оболочки, которые возвращают базовый тип.
В действительности C++ не видит связи между функциями, возвращающими базовые и производные указатели. Только virtual
таким образом функции могут быть "преобразованы", а фабрики не могут быть виртуальными. (Или, по крайней мере, в C++ фабричные объекты с виртуальными функциями не приветствуются. Это было бы типично для Java, но здесь лучше использовать функцию-обертку.)
Поскольку списки инициализаторов все еще находятся в зачаточном состоянии, один из способов отладки (и обхода потенциальных ошибок компилятора, которые могут все еще существовать) - это вручную приводить вещи по мере возможности:
const std::map<std::string, factory_func_t> factory_funcs = {
{ SphereFactory::ID, (factory_func_t) &SphereFactory::create_sphere },
{ QuadFactory::QUAD_ID, (factory_func_t) &QuadFactory::create_quad },
{ QuadFactory::PQUAD_ID, (factory_func_t) &QuadFactory::create_pquad }
};