C++ 继承与专门化

C++ 继承与专门化,c++,inheritance,specialization,C++,Inheritance,Specialization,考虑以下两种使用场景(正如您所看到的,即最终用户只对使用Vector2\u t和Vector3\u t)感兴趣): [1] 继承: template<typename T, size_t N> struct VectorBase { }; template<typename T> struct Vector2 : VectorBase<T, 2> { }; template<typename T> struct Vector3 : Vector

考虑以下两种使用场景(正如您所看到的,即最终用户只对使用
Vector2\u t
Vector3\u t
)感兴趣):

[1] 继承:

template<typename T, size_t N> struct VectorBase
{
};

template<typename T> struct Vector2 : VectorBase<T, 2>
{
};

template<typename T> struct Vector3 : VectorBase<T, 3>
{
};

typedef Vector2<float> Vector2_t;
typedef Vector3<float> Vector3_t;
template<typename T, size_t N> struct VectorBase 
{
};

template<typename T> struct VectorBase<T, 2>
{
};

template<typename T> struct VectorBase<T, 3>
{
};

template<typename T, size_t N> struct Vector : VectorBase<T, N>
{
};
模板结构向量库
{
};
模板结构Vector2:VectorBase
{
};
模板结构Vector3:VectorBase
{
};
类型定义向量2向量2_t;
类型定义向量3向量3_t;
[2] 专业化:

template<typename T, size_t N> struct Vector
{
};

template<typename T> struct Vector<T, 2>
{
};

template<typename T> struct Vector<T, 3>
{
};

typedef Vector<float, 2> Vector2_t;
typedef Vector<float, 3> Vector3_t;
模板结构向量
{
};
模板结构向量
{
};
模板结构向量
{
};
typedef矢量2_t;
typedef矢量3_t;
我拿不定主意哪种解决办法更好。 继承的明显优点是在派生类中重用代码;一个可能的缺点是性能(更大的尺寸,用户可能通过值传递,等等)。 专业化似乎避免了这一切,但代价是我不得不重复多次


我还遗漏了哪些其他优点/缺点?在您看来,我应该选择哪条路线?

如果您过度使用模板专门化,您可能需要重新考虑您的设计。考虑到您将它隐藏在
typedef
后面,我怀疑您是否需要它。

使用继承和私有继承。不要使用任何虚拟函数。由于使用私有继承,您没有is-a,因此没有人能够使用指向派生子类的baseclas指针,并且在按值传递时不会出现切片问题

这使您能够充分利用这两个方面的优势(事实上,这也是大多数库实现许多STL类的方式)

来自(讨论std::vector,而不是您的Vector类):

向量
被声明为具有
向量的私有基
。 放置新元素的所有函数 在向量中,例如
push_back()
,调用 在这个私有基础上的等价函数, 因此,我们的
向量在内部使用
矢量
用于存储。所有功能 从向量返回一个元素,例如
front()
,在 调用等价函数的结果 在私人基地。因为只有这样才能得到一个 指向
向量的指针
(除了 故意的危险伎俩)是通过 由vector提供的接口是安全的 将
void*
静态强制转换回
T*
(或将
void*&
返回到
T*&
,依此类推)


一般来说,如果STL是这样做的,那么它看起来是一个不错的模拟模型。

我认为,您最终想要的是用户类型

Vector<T, N>
并实现仅依赖于N是适当基类中某个特定值的少数函数。您可以在其中添加受保护的析构函数,以防止用户通过指向
VectorBase
的指针删除
Vector
的实例(通常他们甚至不能命名
VectorBase
:将这些基放在一些实现名称空间中,如
detail

另一个想法是将此解决方案与另一个答案中提到的解决方案相结合。私下继承(而不是像上面那样公开),并将包装函数添加到调用基类实现的派生类中

另一个想法是只使用一个类,然后使用
enable_if
(使用
boost::enable_if
)为
N
的特定值启用或禁用它们,或者使用更简单的int类型转换器

struct anyi { };
template<size_t N> struct i2t : anyi { };

template<typename T, size_t N> struct Vector
{
    // forward to the "real" function
    void some_special_function() { some_special_function(i2t<N>()); }

private:
    // case for N == 2
    void some_special_function(i2t<2>) {
        ...
    }

    // case for N == 3
    void some_special_function(i2t<3>) {
        ...
    }

    // general case
    void some_special_function(anyi) {
        ...
    }
};
struct anyi{};
模板结构i2t:anyi{};
模板结构向量
{
//前进到“真实”功能
void some_special_function(){some_special_function(i2t());}
私人:
//N==2的情况
无效某些特殊函数(i2t){
...
}
//N==3的情况
无效某些特殊函数(i2t){
...
}
//一般情况
无效某些特殊函数(anyi){
...
}
};

这样,它对
Vector
的用户是完全透明的。它也不会为执行空基类优化(非常常见)的编译器增加任何空间开销

继承只能用于建模“is-a”。专业化将是更干净的选择。如果出于任何原因需要或想要使用继承,至少要将其设置为私有继承或受保护继承,这样就不会从具有公共非虚拟析构函数的类中公开继承

是的,模板元编程的家伙总是这样做

 struct something : something_else {};

但是那些
something
s是元函数,不打算用作类型。

最完整的答案。非常感谢。很好很酷的回答。需要上师才能得到如此优雅的答案。我怀疑你建议在单个类模板中启用_if是最好的方法——当然,不同向量维度的模板类之间的唯一区别是构造函数中的参数数量?是的,启用_if完全是“零成本”:)这现在希望消除那些i2t对象。虽然它们应该很容易优化掉。一个重载op的小表达式模板,可以用来向ctor提供初始化元素。或者使用op+=比如boost::assign也可以。我想他可能想添加一些东西,比如只为一些N(比如N==3)添加的叉积,他可以使用这个特殊的函数(i2t)或者在吹毛求疵时启用:严格地说,你确实有“is a”,但“is a”不是公共的。例如,
std:vector
是一种
std:vector
,作为实现具有良好空间效率的事物的手段。但这“是一个”私有的,就任何外部代码而言,
std:vectory
不是一个
std:vectory
,因为它无法“看到”关系。严格来说,你确实有is-a,但实际上你没有:)