我正在尝试实现一个模板结构,它允许我在同一个数据结构上保存两种数据类型(事件彼此不兼容)。我知道有一些boost :: any已经做了类似于我想要实现的东西,但我无法为我的项目添加提升。
到目前为止我所获得的内容在以下代码中可用于MSVC,但是甚至不能使用Clang在Xcode上编译,告诉我Cannot specialize a function 'Get' within class scope
每个Get
方法的特殊化我的结构。
我正在努力实现的目标是什么(提升它是如此,我认为它是)?如何修复我的代码才能使其正常工作?
template<typename TypeA, typename TypeB>
struct Either
{
public:
// ...
// CODE NOT USEFUL TO THE QUESTION HERE
// ...
/** Returns true when the value is meaningful; false if calling GetValueA() or GetValueB() is undefined. */
bool IsSet() const { return _typeASet || _typeBSet; }
inline explicit operator bool() const { return _typeASet || _typeBSet; }
/** Returns true when the value is meaningful; false if calling GetValueA() is undefined. */
bool IsASet() const { return _typeASet; }
/** Returns true when the value is meaningful; false if calling GetValueB() is undefined. */
bool IsBSet() const { return _typeBSet; }
/** Fallback for wrong type */
template<typename T>
const T& Get() const
{
static_assert(false, "Cannot get a value which is neither TypeA nor TypeB");
}
/** Fallback for wrong type */
template<typename T>
T& Get()
{
static_assert(false, "Cannot get a value which is neither TypeA nor TypeB");
}
/** Specialize to get TypeA */
template<>
const TypeA& Get() const
{
if(!IsASet())
throw "Invalid operation";
return *(TypeA*)&_valueA;
}
/** Specialize to get TypeA */
template<>
TypeA& Get()
{
if(!IsASet())
throw "Invalid operation";
return *(TypeA*)&_valueA;
}
/** Specialize to get TypeB */
template<>
const TypeB& Get() const
{
if(!IsBSet())
throw "Invalid operation";
return *(TypeB*)&_valueB;
}
/** Specialize to get TypeB */
template<>
TypeB& Get()
{
if(!IsBSet())
throw "Invalid operation";
return *(TypeB*)&_valueB;
}
private:
/** Indicates that the TypeA values is the one being */
bool _typeASet;
/** Indicates that the TypeB values is the one being */
bool _typeBSet;
/** Placeholder for TypeA value */
TypeCompatibleBytes<TypeA> _valueA;
/** Placeholder for TypeB value */
TypeCompatibleBytes<TypeB> _valueB;
};
我想保持以下语法:
Either<int, float> value(5); // Holds the int value
int intVal = value.Get<int>(); // Works returning 5
float fltVal = value.Get<float>(); // Compiles but throws exeption (the in value is the valid one)
std::string strVal = value.Get<std::string>(); // Doesn't compile due to static_assert inside Get
答案 0 :(得分:1)
问题是你不能在成员范围内放置成员模板的专业化;他们需要在命名空间范围内。遗憾的是,您不能仅仅将这些特化项移到类外,因为成员模板的特化还需要专门化包含类模板。解决方法是改为标记调度:
template <typename T> struct type{};
const TypeA& Get(type<TypeA>) const
{
if(!IsASet())
throw "Invalid operation";
return *(TypeA*)&_valueA;
}
TypeA& Get(type<TypeA>)
{
if(!IsASet())
throw "Invalid operation";
return *(TypeA*)&_valueA;
}
const TypeB& Get(type<TypeB>) const
{
if(!IsBSet())
throw "Invalid operation";
return *(TypeB*)&_valueB;
}
TypeB& Get(type<TypeB>)
{
if(!IsBSet())
throw "Invalid operation";
return *(TypeB*)&_valueB;
}
template<typename T>
T& Get()
{
return Get(type<T>{});
}
template<typename T>
const T& Get() const
{
return Get(type<T>{});
}
这提供了单独的重载而不是特化,然后通过传递标记作为参数从重载中进行选择。请注意,我没有尝试编译此代码,但它应该给你一个jist。
答案 1 :(得分:1)
这个怎么样:
template <typename T, typename A, typename B>
struct Get; // leave unimplemented!
template <typename TypeA, typename TypeB>
struct Either
{
enum State
{
E,
A,
B,
} state;
unsigned char data[sizeof(TypeA) > sizeof(TypeB) ? sizeof(TypeA) : sizeof(TypeB)];
template <typename T, typename A, typename B>
friend struct Get;
public:
inline explicit operator bool() const { return isSet(); }
bool isSet() const { return state != E; }
bool isASet() const { return state == A; }
bool isBSet() const { return state == B; }
template<typename T>
T& get() const
{
return Get<T, TypeA, TypeB>::get(this);
}
Either& operator=(TypeA const& a)
{
if(isBSet())
{
((TypeB*)data)->~TypeB();
}
if(isASet())
{
*(TypeA*)data = a;
}
else
{
new (data)TypeA(a);
state = A;
}
return *this;
}
Either& operator=(TypeB const& b)
{
if(isASet())
{
((TypeA*)data)->~TypeA();
}
if(isBSet())
{
*(TypeB*)data = b;
}
else
{
new (data)TypeB(b);
state = B;
}
return *this;
}
};
template <typename A, typename B>
struct Get<A, A, B>
{
static A& get(Either<A, B> const* e)
{
if(e->isASet())
{
return *(A*)e->data;
}
throw "Invalid operation";
}
};
template <typename A, typename B>
struct Get<B, A, B>
{
static B& get(Either<A, B> const* e)
{
if(e->isBSet())
{
return *(B*)e->data;
}
throw "Invalid operation";
}
};
我避免使用包含非POD数据类型的联合,而是使用一个简单的unsigned char数组来保存数据,通过放置构造函数复制数据。
要应对的另一个问题是显式模板特化需要驻留在命名空间范围内。嗯,很好,让我们在命名空间范围内移动它们 - 创建一个模板类,在两个特化中提供适当的getter,并从内部模板函数调用它。至于坏数据类型,没有可用的专业化,编译将适当失败。
当前实现依赖于复制构造函数和赋值运算符可用于这两种类型,但是......
int main (void)
{
Either<std::string, std::vector<int> > e;
e = "hello";
std::cout << e.get<std::string>() << std::endl;
e = std::vector<int>();
e.get<std::vector<int> >().push_back(10);
std::cout << e.get<std::vector<int> >().back() << std::endl;
Either<int, double > ee;
ee = 12;
std::cout << ee.get<int>() << std::endl;
ee = 7.0;
std::cout << ee.get<double>() << std::endl;
return 0;
}
嗯,这个测试至少是成功的......