我创建了一个使用可变参数模板和自动注册的工厂。当尝试创建一个自动注册的类型 - 通过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;
}
答案 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
会推断为T
或T&
(从不T&&
)。语法Args&&
生成T&
或T&&
。因此所需的更改是:
typedef std::unique_ptr<MyType>(*create_func)(Args&&...);