具有移动语义的 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
被推断为 T
或 T&
(永远不会 T&&
)。语法 Args&&
生成 T&
或 T&&
。所以所需的更改是:
typedef std::unique_ptr<MyType>(*create_func)(Args&&...);
我创建了一个使用可变参数模板和自动注册的工厂。当我尝试创建一个自动注册的类型时,我在调用构造函数(移动参数)时遇到了 运行 次关于移动语义的代码崩溃 - 通过工厂。
有人可以向我解释导致 运行 时间崩溃的问题以及如何纠正它吗?
我使用的是 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
被推断为 T
或 T&
(永远不会 T&&
)。语法 Args&&
生成 T&
或 T&&
。所以所需的更改是:
typedef std::unique_ptr<MyType>(*create_func)(Args&&...);