我正在尝试设计一个系统,用户可以将自己的类定义为许多预定义组件的集合,然后让这个类与我提供的算法一起工作。我试图用编译时和/或基于模板的方法来做这个,而不是运行时多态或虚函数,因为在这种情况下性能很重要。
例如,考虑我有许多可用于构建3D顶点的组件。我将这些组件定义为Position,Normal,Color等,然后用户将能够(通过多重继承,组合或什么?)来定义一个顶点,例如PositionAndColorVertex,它只有位置和颜色但没有法线。现在,我提供了一个函数,它对一百万个这些顶点的向量进行一些处理:
template<typename UsersVertexType>
void myProvidedAlgorithm(std::vector<UsersVertexType> input)
{
if(vertex has a position)
//do stuff with position
if(vertex has a normal)
//do stuff with normal
if(vertex has a color)
//do stuff with color
}
现在,我不知道UsersVertexType会是什么样子,但它将从我的组件构建。我的函数需要对每个组件执行某些操作,但前提是它们存在。什么是表达这种优雅和快速(编译时)的方式?
当然,我可以为每种类型定义一个基类,让用户从所需的基类继承,然后使用dynamic_cast来检查实现了哪些组件,但这正是我想要的那种运行时方法避免。也许我可以在编译时检查这个继承关系(编译器应该知道UsersVertexType实际上是什么,对吧?)。
也许我的组件应该使用C ++概念或策略来表达?我也看到了关于mixins的讨论,但不确定这些是否有用。用户类应该使用多重继承还是组合?也许我应该以某种方式在users类中获得一组标志,指出它包含什么?你会如何设计这个系统?
感谢您的任何见解!
注意:与previous question有相似之处,但在这里我退后一步,寻找更高级别的设计选项/备选方案。
答案 0 :(得分:4)
通常的模式是使用type_traits
,和/或使用可以专用于UserVertexType的独立函数模板。
添加一些SFINAE过载选择魔法和瞧:你发明了模板元编程和Boost Graph Library。
小想法样本:
template <typename VertexType>
struct vertex_traits // default vertex_traits:
{
typename unused_tag position_type;
enum {
hasNormal = 0 // treat as 'boolean'
hasColor = 0
};
};
template <typename V> vertex_traits<V>::position_type& position(V&);
这个想法是,通过将通用的东西定义在外面这个类,你不会对用户选择的顶点类型施加任何不必要的限制(他们可能只是使用std :: pair,他们可能只是传递第三方类型并将其装饰以用于您的图库等。)
Google:&#34;非会员功能如何改善封装&#34; (作者Scott Meyer)
答案 1 :(得分:2)
特征和模板专业化。
#include <iostream>
template <typename V> struct traits; // Primary template.
然后为具有位置分量的顶点定义一个版本,为没有的顶点定义一个版本:
template <typename Vertex, bool has_position=traits<Vertex>::has_position>
struct some_position_op;
template <typename Vertex> struct some_position_op<Vertex,false> {
void operator() () { std::cout << "has no position.\n"; }
};
template <typename Vertex> struct some_position_op<Vertex,true> {
void operator() () { std::cout << "has position.\n"; }
};
最后,对于您定义的每个顶点类型,实现一个traits类:
struct MyVertexWithPosition {};
template <>
struct traits<MyVertexWithPosition> {
static constexpr bool has_position = true;
};
struct MyVertexWithoutPosition {};
template <>
struct traits<MyVertexWithoutPosition> {
static constexpr bool has_position = false;
};
......玩得开心:
template <typename Vertex>
void shade (Vertex const &vtx) {
some_position_op<Vertex>() ();
}
int main () {
shade (MyVertexWithPosition());
shade (MyVertexWithoutPosition());
}
你也可以专门化功能模板,但必须牺牲shade
功能的一些可读性。