我正在尝试创建一个通用类,该通用类将C库中的结构包装为结构。考虑这两个结构定义。
struct s32 {
int a:
int b;
};
struct s64 {
long a;
long b;
};
我正在尝试确定C ++中是否存在泛型结构,该结构可以透明地围绕这些结构创建包装器,例如,我不必显式编写代码来访问long
或{{ 1}}的变化。
由于我不知道除了构造一个接口并实现这两个版本外,哪种结构会提供这种功能-这只是伪代码。
int
我知道为什么这行不通。模板是在编译时确定的,因此当使用没有实际类型参数的泛型template <typename T, typename I> class SWrapper {
T s;
public:
SWrapper(T _s) : s(_s) {};
I get_a() { return this->s->a; }
}
class S32 : SWrapper<s32*, int> {};
class S64 : SWrapper<s64*, long> {};
int main(void) {
s32 s1 = { 1, 2 };
s64 s2 = { 3, 4 };
SWapper* S = new S32(&s1); // I know this is incorrect
S = new S64(&s2); //
assert(s.get_a() == 3);
}
且使用类型参数时,编译器将无法确定S
的实际含义,这会导致排除其他类型的擦除因此不能将它们都分配到同一地址。
除了C宏或为实现同一接口的两个类实现所有代码外,C ++中是否有某些构造可以产生相同的效果?
[更新]
在考虑了几种模式之后,我决定使用一种我认为“编写成本最低”的模式。这与@JaMiT的答案非常接近。
我看到的好处是:
SWapper
。 再次请考虑以下伪代码。
<>
答案 0 :(得分:1)
S
的问题不在于在编译时确定模板,而是指针不能指向不相关的类型。为您提供没有模板的相同设置:
class A;
class B;
class C * S = ??? // Cannot point to another class.
您可能会看到您要使用的类之间的联系,但是与编译器SWrapper<s32*, int>
和SWrapper<s64*, long>
的区别与A
和B
一样。
为了使指针指向不同的类型,这些类型需要共享一个共同的祖先。当您对从C库获取的结构有足够的限制时,这样做并不难。问题是这些结构具有共同的逻辑结构,但成员类型不同。意见澄清说,成员的类型都可以隐式转换为通用类型。 (具体来说,它们都是某种形式的带符号整数。)因此,可以定义要查看的接口:
class WrapBase {
public:
virtual ~WrapBase() {}
virtual long get_a() = 0;
virtual long get_b() = 0;
};
请注意,成员函数返回long
。这是事物将隐式转换为的类型。如果需要,可以为long long
。
接下来,使用您的模板使此基础适应各种结构:
template <typename T>
class Wrap : public WrapBase {
private:
T & data;
public:
explicit Wrap(T & from_c) : data(from_c) {}
long get_a() { return data.a; }
long get_b() { return data.b; }
};
现在,您可以编写将包装结构作为参数的函数。好吧,大概这就是您想要做的事情。手动调用new
是不可取的,但是它确实提供了一个演示实际多态性的快速演示。
// Demonstration using the question's main() as context:
WrapBase * S = new Wrap<s32>(s1);
std::cout << "s1: {" << S->get_a() << ',' << S->get_b() << "};\n";
delete S;
S = new Wrap<s64>(s2);
std::cout << "s2: {" << S->get_a() << ',' << S->get_b() << "};\n";
delete S;
由于多态性适用于指针,因此它适用于用作函数参数的引用。
上述缺点之一是您必须两次声明所有访问函数。还有其他工作可以从包装的结构中获取值。一个更简单的代码界面(和使用?)将涉及转换而不是包装数据。获取数据时,这涉及较高的前期成本,但访问成本较低。这是否一个选项取决于执行限制,但是可能值得尝试看看它的工作情况。
struct Converted {
long a;
long b;
template <typename T>
explicit Converted(T & source) :
a(source.a),
b(source.b)
{}
};
再次使用long
作为可以隐式转换所有内容的类型。现在,您可以使用更熟悉的访问语法来使用数据。 (如果您仍然想要getter函数(一种合理的设计选择),则编译器应该能够内联它们,这是虚拟函数无法实现的。)
// Demonstration using the question's main() as context:
Converted S{s1};
std::cout << "s1: {" << S.a << ',' << S.b << "};\n";
Converted SS{s2};
std::cout << "s2: {" << SS.a << ',' << SS.b << "};\n";