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

C++ variadic factory with move semantics causes run-time crash

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

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

我使用的是 c++17,所以如果有我应该使用的构造,我也将不胜感激相关建议(示例代码)。

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

编辑:测试的编译器版本是:Visual Studio 2017 with "Version 19.14.26430" and "GCC 7.3"

编辑:删除了代码版本,其中 '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;
}

您的地图存储 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&&...);