注意:我知道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()按预期工作(我不确定)感谢@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这样的东西,它已经过广泛的调试和测试.也就是说,恭喜找出使这项工作成功的关键技巧!