给出以下模板:
template <typename T>
class wrapper : public T {};
Foo
类型的对象与wrapper<Foo>
类型的对象之间的界面或行为有何明显差异?
我已经知道了一个:
wrapper<Foo>
只有一个无效的构造函数,复制构造函数和赋值运算符(如果这些操作在Foo
上有效,它只有那些)。通过在wrapper<T>
中使用一组模板化构造函数将值传递给T构造函数,可以减轻这种差异。但我不确定可能存在哪些其他可检测的差异,或者是否有隐藏它们的方法。
有些人似乎在为这个问题寻求一些背景,所以这是对我的情况的一个(有点简化)解释。
我经常编写具有可调整值的代码,以调整系统的精确性能和操作。我希望通过配置文件或用户界面轻松(低代码开销)方式公开这些值。我正在写一个图书馆,允许我这样做。预期的设计允许使用类似这样的东西:
class ComplexDataProcessor {
hotvar<int> epochs;
hotvar<double> learning_rate;
public:
ComplexDataProcessor():
epochs("Epochs", 50),
learning_rate("LearningRate", 0.01)
{}
void process_some_data(const Data& data) {
int n = *epochs;
double alpha = *learning_rate;
for (int i = 0; i < n; ++i) {
// learn some things from the data, with learning rate alpha
}
}
};
void two_learners(const DataSource& source) {
hotobject<ComplexDataProcessor> a("FastLearner");
hotobject<ComplexDataProcessor> b("SlowLearner");
while (source.has_data()) {
a.process_some_data(source.row());
b.process_some_data(source.row());
source.next_row();
}
}
运行时,这将设置或读取以下配置值:
FastLearner.Epochs
FastLearner.LearningRate
SlowLearner.Epochs
SlowLearner.LearningRate
这是由代码组成的(因为我的用例甚至不是机器学习),但它显示了设计的几个重要方面。可调整的值都被命名,并且可以组织成层次结构。可以通过几种方法对值进行分组,但在上面的示例中,我只显示一种方法:在hotobject<T>
类中包装对象。在实践中,hotobject<T>
包装器有一个相当简单的工作 - 它必须将对象/组名称推送到线程本地上下文堆栈,然后允许构造T
对象(此时构造hotvar<T>
值并检查上下文堆栈以查看它们应该在哪个组中,然后弹出上下文堆栈。
这样做如下:
struct hotobject_stack_helper {
hotobject_stack_helper(const char* name) {
// push onto the thread-local context stack
}
};
template <typename T>
struct hotobject : private hotobject_stack_helper, public T {
hotobject(const char* name):
hotobject_stack_helper(name) {
// pop from the context stack
}
};
据我所知,这种情况下的施工顺序定义明确:
hotobject_stack_helper
(将名称推送到上下文堆栈)T
- 包括构建每个T
成员(hotvars)hotobject<T>
构造函数的主体,弹出上下文堆栈。所以,我有工作代码来做这件事。然而,还有一个问题,那就是:通过使用这种结构,我可能会为自己进一步的问题带来什么问题。这个问题在很大程度上减少了我实际问的问题:hotobject将如何与T本身不同?
答案 0 :(得分:3)
奇怪的问题,因为你应该问一些关于你的具体用法的问题(“我想做什么,这对我有什么帮助或伤害我”),但我想一般情况:
wrapper<T>
不是T
,所以:
T
那样构造。 (如你所知。)T
。T
有权访问。我确信还有更多,但前两个涵盖了很多。
答案 1 :(得分:2)
假设你有:
class Base {};
class Derived : Base {};
现在你可以说:
Base *basePtr = new Derived;
然而,你不能说:
wrapper<Base> *basePtr = new wrapper<Derived>();
即使它们的类型参数可能具有继承关系,通过专门化模板生成的两种类型也没有任何继承关系。
答案 2 :(得分:0)
对象的引用是可转换的(给定访问权限)对基类子对象的引用。有一个语法糖可以调用隐式转换,允许您将对象视为基础的实例,但这确实是正在发生的事情。不多也不少。
因此,差异并不难发现。它们(几乎)是完全不同的东西。 “is-a”关系和“has-a”关系之间的区别在于指定成员名称。
至于隐藏基类,我认为你无意中回答了自己的问题。通过指定private
(或省略public
的{{1}})来使用私有继承,并且这些转换不会在类本身之外发生,并且没有其他类能够告诉它基地甚至存在。
答案 3 :(得分:0)
如果您的继承类有自己的成员变量(或至少一个),那么
sizeof(InheritedClass) > sizeof(BaseClass)