具有移动语义的C ++可变参数工厂导致运行时崩溃

时间:2018-06-17 17:41:13

标签: c++ templates factory move variadic

我创建了一个使用可变参数模板和自动注册的工厂。当尝试创建一个自动注册的类型 - 通过Factory时,我在调用构造函数(移动了一个参数)时遇到了关于移动语义的运行时崩溃问题。

有人可以向我解释导致运行时崩溃的问题是什么以及如何纠正它?

我正在使用c ++ 17,所以如果有我应该使用的构造,我也会很感激建议(示例代码)。

编辑:当~Base()析构函数执行时发生'访问冲突'崩溃 - 但是,当Base()构造函数完成时,成员“_moveString”为NULL。因此,内存显然没有在构造函数然后在销毁期间导致崩溃。

编辑:测试的编译器版本是:带有“版本19.14.26430”和“GCC 7.3”的Visual Studio 2017

编辑:删除了代码版本,其中'Base :: _ moveString'是'boost :: optional'到'std :: unique_ptr'

编辑:改变代码以确保确切的函数类型匹配,并且只有一个参数,我们执行“std :: move”(这是问题的焦点)

#include <string>
#include <memory>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>

using namespace std;

class Base
{
public:
    Base(unique_ptr<string>&& moveString)
        :
        _moveString(move(moveString)) // why is moveString empty here? Should have value "moveString"
    {
    }

    virtual ~Base() = default;
    virtual void DoSomething() const = 0;

protected:
    unique_ptr<string> _moveString;
};

class Factory final
{
public:
    template<typename My_Type, typename... Args>
    static unique_ptr<My_Type> Create(Args&&... args)
    {
        unique_ptr<My_Type> type = nullptr;
        auto iter = GetCreateFunctions().find(typeid(My_Type));

        if (GetCreateFunctions().end() != iter)
        {
            typedef unique_ptr<My_Type>(*create_func)(Args...);
            auto create = reinterpret_cast<create_func>(iter->second);
            //auto a = (get<1>(forward_as_tuple(forward<Args>(args)...))).get(); // DEBUGGING
            type = create(forward<Args>(args)...);
        }

        return type;
    }

    template<typename My_Type, typename Func>
    static bool Register(Func func)
    {
        bool isRegistered = false;

        if (GetCreateFunctions().end() == GetCreateFunctions().find(typeid(My_Type)))
        {
            GetCreateFunctions()[typeid(My_Type)] = reinterpret_cast<void*>(func);
            isRegistered = true;
        }

        return isRegistered;
    }

private:
    static unordered_map<type_index, void*>& GetCreateFunctions()
    {
        static unordered_map<type_index, void*> map;
        return map;
    }
};

class Derived final : public Base
{
public:
    Derived(unique_ptr<string>&& moveString)
        :
        Base(move(moveString))
    {
    }

    ~Derived() = default;

    void DoSomething() const override
    {
        if (_moveString)
        {
            // do something...
        }
    }

private:
    static const bool _isRegistered;

    static unique_ptr<Derived> Create(unique_ptr<string>&& moveString)
    {
        return make_unique<Derived>(move(moveString));
    }
};

const bool Derived::_isRegistered = Factory::template Register<Derived>(&Derived::Create);


int main(int argc, char** argv)
{
    string moveString = "moveString";
    unique_ptr<Base> myBase = Factory::template Create<Derived>(make_unique<string>(move(moveString)));

    if (myBase)
        printf("Success\n");

    return 0;
}

1 个答案:

答案 0 :(得分:1)

您的地图存储void *reinterpret_cast函数指针。使用函数指针的代码是:

 // Args... is deduced from arguments to Create()
 typedef std::unique_ptr<MyType>(*create_func)(Args...);
 auto create = reinterpret_cast<create_func>(iter->second);

但是,您在地图中输入的值是:

的地址
static std::unique_ptr<Derived> Create(unique_ptr<string>&& moveString)

这会导致未定义的行为。通过函数指针调用函数时,被调用函数必须具有与函数指针指向的完全相同的类型。 (参考:C ++ 17 [expr.call] / 1)。

  • 实际功能类型为unique_ptr<Derived>(unique_ptr<string>&&)
  • 您投射到的类型是指向unique_ptr<Derived>(unique_ptr<string>)
  • 的指针

这与参数类型不匹配。

使用转发引用时,Args会推断为TT&(从不T&&)。语法Args&&生成T&T&&。因此所需的更改是:

 typedef std::unique_ptr<MyType>(*create_func)(Args&&...);