Список инициализатора 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 }
};
Другие вопросы по тегам