我正在处理C ++中的union,我希望有一个函数模板,可以根据模板参数访问活动的union成员。
代码就像(doSomething只是一个例子):
union Union {
int16_t i16;
int32_t i32;
};
enum class ActiveMember {
I16
, I32
}
template <ActiveMember M>
void doSomething(Union a, const Union b) {
selectMemeber(a, M) = selectMember(b, M);
// this would be exactly (not equivalent) the same
// that a.X = b.X depending on T.
}
为了实现这一点,我只发现了一些糟糕的黑客行为,比如专业化,或者是一种不同类的访问和分配方式。
我错过了什么,这样的事情应该用其他方法来做?
答案 0 :(得分:6)
可能性1
而不是使用枚举,您可以使用简单的结构来选择成员:
typedef short int16_t;
typedef long int32_t;
union Union {
int16_t i16;
int32_t i32;
};
struct ActiveMemberI16 {};
struct ActiveMemberI32 {};
template <typename M>
void doSomething(Union& a, Union b) {
selectMember(a, M()) = selectMember(b, M());
// this would be exactly (not equivalent) the same
// that a.X = b.X depending on T.
}
int16_t& selectMember(Union& u, ActiveMemberI16)
{
return u.i16;
}
int32_t& selectMember(Union& u, ActiveMemberI32)
{
return u.i32;
}
int main(int argc, char* argv[])
{
Union a,b;
a.i16 = 0;
b.i16 = 1;
doSomething<ActiveMemberI16>(a,b);
std::cout << a.i16 << std::endl;
b.i32 = 3;
doSomething<ActiveMemberI32>(a,b);
std::cout << a.i32 << std::endl;
return 0;
}
这需要为union中的每个成员定义struct和selectMember方法,但至少可以在许多其他函数中使用selectMember。
请注意,我将参数转换为引用,如果不合适,您可以调整它。
可能性2
通过将union指针强制转换为所需的类型指针,您可以使用单个selectMember函数。
typedef short int16_t;
typedef long int32_t;
union Union {
int16_t i16;
int32_t i32;
};
template <typename T>
T& selectMember(Union& u)
{
return *((T*)&u);
}
template <typename M>
void doSomething(Union& a, Union b) {
selectMember<M>(a) = selectMember<M>(b);
// this would be exactly (not equivalent) the same
// that a.X = b.X depending on T.
}
int _tmain(int argc, _TCHAR* argv[])
{
Union a,b;
a.i16 = 0;
b.i16 = 1;
doSomething<int16_t>(a,b);
std::cout << a.i16 << std::endl;
b.i32 = 100000;
doSomething<int32_t>(a,b);
std::cout << a.i32 << std::endl;
return 0;
}
答案 1 :(得分:3)
我不确定为什么你认为模板特化是一个“糟糕的黑客”,但是在C ++中没有“静态if”这样的东西,所以如果你希望你的编译器根据结果区分生成的代码在编译时评估的表达式,您需要定义模板的不同专用版本。
以下是如何定义它:
#include <iostream>
using namespace std;
union Union {
int16_t int16;
int32_t int32;
};
enum class ActiveMember {
INT16
, INT32
};
// Declare primary template
template <ActiveMember M>
void doSomething(Union a, const Union b);
// First specialization
template <>
void doSomething<ActiveMember::INT16>(Union a, const Union b)
{
a.int16 = b.int16;
// Do what you want here...
cout << "int16" << endl;
}
// Second specialization
template <>
void doSomething<ActiveMember::INT32>(Union a, const Union b)
{
a.int32 = b.int32;
// Do what you want here...
cout << "int32" << endl;
}
这就是你如何使用它。
int main()
{
Union u1, u2;
u1.int32 = 0;
u2.int32 = 0;
doSomething<ActiveMember::INT16>(u1, u2);
doSomething<ActiveMember::INT32>(u1, u2);
return 0;
}
答案 2 :(得分:1)
我能想到的唯一解决方案是在联合中添加运算符:
union Union
{
char c;
short s;
int i;
float f;
double d;
operator char() const { return c; }
operator short() const { return s; }
operator int() const { return i; }
operator float() const { return f; }
operator double() const { return d; }
template <typename T> operator T() const { /* invalid conversion */ T t; return t; }
Union &operator =(char ac) { c = ac; return *this; }
Union &operator =(short as) { s = as; return *this; }
Union &operator =(int ai) { i = ai; return *this; }
Union &operator =(float af) { f = af; return *this; }
Union &operator =(double ad) { d = ad; return *this; }
template <typename T> Union &operator =(T at) { /* invalid asignement */ return *this; }
};
它允许您在联合作为some type:
时控制联合的行为template <typename T>
void doSomething(Union a, const Union b)
{
// call the 'b' conversion operator and the 'a' asignment operator.
a = static_cast<T>(b);
}
int main(int argc, char **argv)
{
Union a, b;
doSomething<int>(a, b); // calls a.i = b.i
doSomething<char>(a, b); // calls a.c = b.c
return 0;
}
运算符的模板版本与无效转换匹配。