我的目标是学习更多关于C++通用编程的知识。所以,我听说您能做的最棘手的事情之一就是创建一个变体类。这花了我一段时间,我需要阅读和研究一些实现:
#ifndef VARIANT_HPP_INCLUDED
#define VARIANT_HPP_INCLUDED
#include <cstddef>
#include <typeindex>
#include <type_traits>
#include <iostream>
namespace spaceengine
{
namespace utils
{
template<typename... Ts>
struct VariantHelper;
template<typename F, typename... Ts>
struct VariantHelper<F, Ts...>
{
inline static void Destroy(const std::type_index typeIndex, void* data)
{
if(typeIndex == std::type_index(typeid(F)))
reinterpret_cast<F*>(data)->~F();
else
VariantHelper<Ts...>::Destroy(typeIndex,data);
}
inline static void Move(const std::type_index oldTypeIndex, void* oldV, void* newV)
{
if(oldTypeIndex == std::type_index(typeid(F)))
new (newV) F(std::move(*reinterpret_cast<F*>(oldV)));
else
VariantHelper<Ts...>::Move(oldTypeIndex,oldV,newV);
}
inline static void Copy(const std::type_index oldTypeIndex, const void* oldV, void* newV)
{
if(oldTypeIndex == std::type_index(typeid(F)))
new (newV) F(*reinterpret_cast<const F*>(oldV));
else
VariantHelper<Ts...>::Copy(oldTypeIndex,oldV,newV);
}
};
template<>
struct VariantHelper<>
{
inline static void Destroy(const std::type_index typeIndex, void* data){}
inline static void Move(const std::type_index oldTypeIndex, void* oldV, void* newV) {}
inline static void Copy(const std::type_index oldTypeIndex, const void* oldV, void* newV) {}
};
template<typename... Ts>
class Variant
{
public:
Variant() : m_typeIndex(InvalidType()){}
Variant(const Variant<Ts...>& old) : m_typeIndex(old.m_typeIndex)
{
HelperType::Copy(old.m_typeIndex, &old.m_data, &m_data);
}
Variant(Variant<Ts...>&& old) : m_typeIndex(old.m_typeIndex)
{
HelperType::Move(old.m_typeIndex, &old.m_data, &m_data);
}
Variant<Ts...>& operator=(Variant<Ts...> old)
{
m_typeIndex = old.m_typeIndex;
Variant<Ts...> temp(*this);
HelperType::Copy(old.m_typeIndex,&old.m_data,&m_data);
HelperType::Copy(temp.m_typeIndex,&temp.m_data,&old.m_data);
return *this;
}
template<typename T>
bool Is() const
{
return (m_typeIndex == std::type_index(typeid(T)));
}
bool IsValid() const
{
return (m_typeIndex != std::type_index(typeid(InvalidType())));
}
std::type_index GetTypeIndex() const { return m_typeIndex; }
template<typename T, typename... Args>
void Set(Args&&... args)
{
HelperType::Destroy(m_typeIndex,&m_data);
new (&m_data) T(std::forward<Args>(args)...);
m_typeIndex = std::type_index(typeid(T));
}
template<typename T>
const T& Get() const
{
if(m_typeIndex == std::type_index(typeid(T)))
return *reinterpret_cast<const T*>(&m_data);
else
throw std::bad_cast();
}
template<typename T>
T& Get()
{
if(m_typeIndex == std::type_index(typeid(T)))
return *reinterpret_cast<T*>(&m_data);
else
throw std::bad_cast();
}
~Variant()
{
HelperType::Destroy(m_typeIndex,&m_data);
}
private:
using DataType = typename std::aligned_union<1,Ts...>::type;
using HelperType = VariantHelper<Ts...>;
static inline std::type_index InvalidType()
{
return std::type_index(typeid(void));
}
std::type_index m_typeIndex;
DataType m_data;
};
}
}
#endif // VARIANT_HPP_INCLUDED我真正想要的是:
Variant通常不需要的,而这个类不提供?Variant类是堆栈分配的,还是我误解了新布局做了什么?让我烦恼的事情之一是boost::variant类非常大;而且看起来我的小得多的代码完成了我所需要的100%的任务。但我无法很好地破解boost::variant,找出它的作用,而我的却不这样做。
发布于 2016-05-07 08:18:53
我对布局新的使用是否意味着我的变量类是堆栈分配的,还是我误解了新的布局做了什么?
这确实意味着,使用std::union提供足够的分配空间的实现似乎是有效的。
几乎至少,因为Variant::Set()的实现没有检查类型。因此,如果不小心传递了一个没有在Ts中覆盖的类型,则分别需要更多的分配空间,这将破坏内存。
但我无法解读boost::变体,足以弄清楚它能做什么,而我的却做不到。
例如,boost实现提供了一个您的实现没有提供的从不空置担保。您正在显式处理变体处于无效状态的情况。
在Variant::Set()中,您正在显式地销毁旧m_data,但在调用新构造函数之前,您不会将m_typeIndex恢复到安全状态。如果新的构造函数将抛出,它将把m_typeIndex保留在原来的值上,这是完全错误的。
考虑添加一个Variant::Unset()方法,它处理保存破坏,并保证显式地将m_typeIndex设置为无效类型。
这也会导致对无效状态调用析构函数。
VariantHelper模板中的递归。不会说谎-这很难追踪。一个评论应该已经到位,解释你要做什么。
https://codereview.stackexchange.com/questions/127372
复制相似问题