实现"变体"类

Sta*_*ked 25 c++

注意:我知道boost::variant,但我对设计原则感到好奇.这个问题主要是为了自我教育.

原帖

在我目前的工作中,我找到了一个旧的变体类实现.它是用a实现的union,只能支持少数几种数据类型.我一直在考虑如何设计改进版本.经过一些修修补补后,我得到了似乎有用的东西.但是我想知道你对它的看法.这里是:

#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/shared_ptr.hpp>

class Variant
{
public:
    Variant() { }

    template<class T>
    Variant(T inValue) :
        mImpl(new VariantImpl<T>(inValue)),
        mClassName(typeid(T).name())
    {
    }

    template<class T>
    T getValue() const
    {
        if (typeid(T).name() != mClassName)
        {
            throw std::logic_error("Non-matching types!");
        }

        return dynamic_cast<VariantImpl<T>*>(mImpl.get())->getValue();
    }

    template<class T>
    void setValue(T inValue)
    {
        mImpl.reset(new VariantImpl<T>(inValue));
        mClassName = typeid(T).name();
    }

private:
    struct AbstractVariantImpl
    {
        virtual ~AbstractVariantImpl() {}
    };

    template<class T>
    struct VariantImpl : public AbstractVariantImpl
    {
        VariantImpl(T inValue) : mValue(inValue) { }

        ~VariantImpl() {}

        T getValue() const { return mValue; }

        T mValue;
    };

    boost::shared_ptr<AbstractVariantImpl> mImpl;
    std::string mClassName;
};

int main()
{
    // Store int
    Variant v(10);
    int a = 0;
    a = v.getValue<int>();
    std::cout << "a = " << a << std::endl;

    // Store float
    v.setValue<float>(12.34);
    float d = v.getValue<float>();
    std::cout << "d = " << d << std::endl;

    // Store map<string, string>
    typedef std::map<std::string, std::string> Mapping;
    Mapping m;
    m["one"] = "uno";
    m["two"] = "due";
    m["three"] = "tre";
    v.setValue<Mapping>(m);
    Mapping m2 = v.getValue<Mapping>();
    std::cout << "m2[\"one\"] = " << m2["one"] << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出正确:

a = 10
d = 12.34
m2["one"] = uno
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • 这个实现是否正确?
  • 动态演员是否会getValue()按预期工作(我不确定)
  • 我应该将T作为const引用返回吗?或者我可以依靠返回值优化来启动?
  • 还有其他问题或建议吗?

更新

感谢@templatetypedef的建议.此更新版本仅用于dynamic_cast检查类型是否匹配.由于TypeWrapper类(我从Poco C++项目中无耻地窃取),现在可以避免由const的差异引起的类型不匹配.

所以这是当前版本.它可能包含一些错误,因为我不熟悉修改模板模板上的const/ref的想法.我明天会有新的面貌.

template <typename T>
struct TypeWrapper
{
    typedef T TYPE;
    typedef const T CONSTTYPE;
    typedef T& REFTYPE;
    typedef const T& CONSTREFTYPE;
};

template <typename T>
struct TypeWrapper<const T>
{
    typedef T TYPE;
    typedef const T CONSTTYPE;
    typedef T& REFTYPE;
    typedef const T& CONSTREFTYPE;
};

template <typename T>
struct TypeWrapper<const T&>
{
    typedef T TYPE;
    typedef const T CONSTTYPE;
    typedef T& REFTYPE;
    typedef const T& CONSTREFTYPE;
};

template <typename T>
struct TypeWrapper<T&>
{
    typedef T TYPE;
    typedef const T CONSTTYPE;
    typedef T& REFTYPE;
    typedef const T& CONSTREFTYPE;
};

class Variant
{
public:
    Variant() { }

    template<class T>
    Variant(T inValue) :
        mImpl(new VariantImpl<typename TypeWrapper<T>::TYPE>(inValue))
    {
    }

    template<class T>
    typename TypeWrapper<T>::REFTYPE getValue()
    {
        return dynamic_cast<VariantImpl<typename TypeWrapper<T>::TYPE>&>(*mImpl.get()).mValue;
    }

    template<class T>
    typename TypeWrapper<T>::CONSTREFTYPE getValue() const
    {
        return dynamic_cast<VariantImpl<typename TypeWrapper<T>::TYPE>&>(*mImpl.get()).mValue;
    }

    template<class T>
    void setValue(typename TypeWrapper<T>::CONSTREFTYPE inValue)
    {
        mImpl.reset(new VariantImpl<typename TypeWrapper<T>::TYPE>(inValue));
    }

private:
    struct AbstractVariantImpl
    {
        virtual ~AbstractVariantImpl() {}
    };

    template<class T>
    struct VariantImpl : public AbstractVariantImpl
    {
        VariantImpl(T inValue) : mValue(inValue) { }

        ~VariantImpl() {}

        T mValue;
    };

    boost::shared_ptr<AbstractVariantImpl> mImpl;
};
Run Code Online (Sandbox Code Playgroud)

tem*_*def 10

这个实现接近正确,但看起来它有一些错误.例如,这段代码:

if (typeid(T).name() != mClassName)
Run Code Online (Sandbox Code Playgroud)

不能保证正常工作,因为.name()函数in type_info不能保证为每种类型返回唯一值.如果你想检查类型是否匹配,你应该使用这样的东西:

if (typeid(*mImpl) == typeid(VariantImpl<T>))
Run Code Online (Sandbox Code Playgroud)

哪个更准确地检查类型是否匹配.当然,您需要注意const问题,因为存储const T和存储T将产生不同的类型.

至于你的问题dynamic_cast,在你描述的情况下你不需要使用,dynamic_cast因为你已经检查确认类型是否匹配.相反,您可以使用a static_cast,因为您已经遇到了错误类型的情况.

更重要的是,你在这里定义的是一个"无限制的变体",它可以保留任何东西,而不仅仅是一小部分限制类型(这是你通常在变体中找到的).虽然我非常喜欢这段代码,但我建议使用像Boost.Any或Boost.Variant这样的东西,它已经过广泛的调试和测试.也就是说,恭喜找出使这项工作成功的关键技巧!