C++ 元编程与继承
C++ metaprogramming vs inheritance
我想知道当我们可以实现相同的功能目标时,在 C++ 中使用元编程(即模板)来对抗继承是否有特别的优势。
例如我们可以做一些事情(我只是想出这样的例子......):
template<typename T>
class Node {
public:
//bla bla
private:
T data;
Node* next;
Node* prev;
};
使用类型特征,我们可以进一步限制特定的 class 个对象。但是我想我们可以用继承做一些非常相似的事情:
class Base {
//bla bla
};
class Der1 : public Base {
//bla bla
};
//.
//.
//.
class DerN : public Base {
//bla bla
};
class Node {
public:
//bla bla
private:
Base* data;
Node* next;
Node* prev;
};
这样的情况有什么好处?当我们可以同时使用两者来实现同一件事时,选择其中一个或另一个的关键因素是什么?
模板:
- 编译时机制(即没有 运行 时间开销)
- 无多态性:Node和Node是完全不同的对象,没有任何关系(所以不能从一种类型转换为另一种类型)
继承通常是一种更灵活的方法。这实际上取决于您需要实现的行为。
一些非常笼统的说法:在性能方面,使用模板的编译时间更长,使用动态多态性的运行时间更长。
使用模板有时在结构上更复杂。这就是为什么即使是一些性能优化的软件也不完全使用模板的原因。但是,我看到一些优化软件尽可能地使用模板,而且速度快得令人难以置信。
像许多此类问题一样,“关键因素”取决于代码的应用。
编译时间、代码复杂度或执行速度哪个更重要?
- 如果您的目的是使 compile-times 更快(由于项目大小或 older-build 服务器),您希望避免使用模板,每个 code-change 模板都会重建所有 source-files template-headers 都包含在.
- 如果您关心执行时间,模板通常允许您生成代码,编译器可以专门针对相关类型优化,但是继承允许 您专门针对每种类型优化 代码(添加vtable-lookup 次)。因此,您需要对两者的跟踪代码进行基准测试。
- 如果您关心 code-complexity(并且对于大多数现代应用程序来说,这是最重要的)这取决于项目中其他开发人员的技能,因为以及您需要的模板专业化数量。
关于最后一点,衡量这需要对超出这个问题范围的任务有复杂的理解,但是我的建议很简单;如果您需要的专业化程度与您的类型一样多,请改用继承。对于像您的示例 node-tree 这样的东西,继承类型将为普通开发人员提供更好的解决方案,但是如果您需要“适用于一切”的方法或函数(例如流 <<
运算符)使用模板将是更好的办法。
tl;博士
除非您需要担心特定的 processing/memory 开销,否则请使用创建 smallest-amount 代码的开销。争取 干净、清晰且易于阅读。
这两个概念没有关系。
继承仅提供运行时多态性 - 具体操作可以应用于任何对象,前提是它是基础对象。
虽然泛型编程允许根据一般概念进行编码 - 即,将此一般操作应用于这些不相关类型的对象。
用例是不同的,而且通常是互补的。 CRTP 模式就是一个示例 - 泛型编程用于提供可继承接口的实现。
With typetraits we can further restrict to a particular class of objects. However i guess we could do something very similar with inheritance:
不是在编译时,你不能。继承的主要缺点之一 - 特别是在寻求限制或专门化基于类型的操作时,这必须在运行时完成 - 这意味着泛型编程的编译器错误现在变成了呈现给用户的运行时错误。
编译器错误是好的 - 在签入代码之前腐烂停止。运行时错误是邪恶的。它们的修复成本(至少)高出两个数量级,并且让您(或您的公司)看起来很糟糕。
我想知道当我们可以实现相同的功能目标时,在 C++ 中使用元编程(即模板)来对抗继承是否有特别的优势。
例如我们可以做一些事情(我只是想出这样的例子......):
template<typename T>
class Node {
public:
//bla bla
private:
T data;
Node* next;
Node* prev;
};
使用类型特征,我们可以进一步限制特定的 class 个对象。但是我想我们可以用继承做一些非常相似的事情:
class Base {
//bla bla
};
class Der1 : public Base {
//bla bla
};
//.
//.
//.
class DerN : public Base {
//bla bla
};
class Node {
public:
//bla bla
private:
Base* data;
Node* next;
Node* prev;
};
这样的情况有什么好处?当我们可以同时使用两者来实现同一件事时,选择其中一个或另一个的关键因素是什么?
模板:
- 编译时机制(即没有 运行 时间开销)
- 无多态性:Node和Node是完全不同的对象,没有任何关系(所以不能从一种类型转换为另一种类型)
继承通常是一种更灵活的方法。这实际上取决于您需要实现的行为。
一些非常笼统的说法:在性能方面,使用模板的编译时间更长,使用动态多态性的运行时间更长。
使用模板有时在结构上更复杂。这就是为什么即使是一些性能优化的软件也不完全使用模板的原因。但是,我看到一些优化软件尽可能地使用模板,而且速度快得令人难以置信。
像许多此类问题一样,“关键因素”取决于代码的应用。
编译时间、代码复杂度或执行速度哪个更重要?
- 如果您的目的是使 compile-times 更快(由于项目大小或 older-build 服务器),您希望避免使用模板,每个 code-change 模板都会重建所有 source-files template-headers 都包含在.
- 如果您关心执行时间,模板通常允许您生成代码,编译器可以专门针对相关类型优化,但是继承允许 您专门针对每种类型优化 代码(添加vtable-lookup 次)。因此,您需要对两者的跟踪代码进行基准测试。
- 如果您关心 code-complexity(并且对于大多数现代应用程序来说,这是最重要的)这取决于项目中其他开发人员的技能,因为以及您需要的模板专业化数量。
关于最后一点,衡量这需要对超出这个问题范围的任务有复杂的理解,但是我的建议很简单;如果您需要的专业化程度与您的类型一样多,请改用继承。对于像您的示例 node-tree 这样的东西,继承类型将为普通开发人员提供更好的解决方案,但是如果您需要“适用于一切”的方法或函数(例如流 <<
运算符)使用模板将是更好的办法。
tl;博士
除非您需要担心特定的 processing/memory 开销,否则请使用创建 smallest-amount 代码的开销。争取 干净、清晰且易于阅读。
这两个概念没有关系。
继承仅提供运行时多态性 - 具体操作可以应用于任何对象,前提是它是基础对象。
虽然泛型编程允许根据一般概念进行编码 - 即,将此一般操作应用于这些不相关类型的对象。
用例是不同的,而且通常是互补的。 CRTP 模式就是一个示例 - 泛型编程用于提供可继承接口的实现。
With typetraits we can further restrict to a particular class of objects. However i guess we could do something very similar with inheritance:
不是在编译时,你不能。继承的主要缺点之一 - 特别是在寻求限制或专门化基于类型的操作时,这必须在运行时完成 - 这意味着泛型编程的编译器错误现在变成了呈现给用户的运行时错误。
编译器错误是好的 - 在签入代码之前腐烂停止。运行时错误是邪恶的。它们的修复成本(至少)高出两个数量级,并且让您(或您的公司)看起来很糟糕。