首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用泛型编程的变量类

使用泛型编程的变量类
EN

Code Review用户
提问于 2016-05-03 11:26:49
回答 1查看 654关注 0票数 10

我的目标是学习更多关于C++通用编程的知识。所以,我听说您能做的最棘手的事情之一就是创建一个变体类。这花了我一段时间,我需要阅读和研究一些实现:

代码语言:javascript
复制
#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

我真正想要的是:

  1. 有什么是Variant通常不需要的,而这个类不提供?
  2. 我还有什么明显的遗漏吗?
  3. 我对placement的使用是否意味着我的Variant类是堆栈分配的,还是我误解了新布局做了什么?

让我烦恼的事情之一是boost::variant类非常大;而且看起来我的小得多的代码完成了我所需要的100%的任务。但我无法很好地破解boost::variant,找出它的作用,而我的却不这样做。

EN

回答 1

Code Review用户

回答已采纳

发布于 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模板中的递归。不会说谎-这很难追踪。一个评论应该已经到位,解释你要做什么。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/127372

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档