继承与专业化
考虑以下两种使用场景(正如您所看到的,即最终用户只会对使用 Vector2_t
和 Vector3_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;
[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;
我无法决定哪个是更好的解决方案。 继承的明显优点是派生类中的代码重用; 一个可能的缺点是性能(更大的尺寸,用户可能按值传递等)。 专业化似乎可以避免这一切,但代价是我不得不多次重复自己。
我还错过了哪些其他优点/缺点?您认为我应该走哪条路?
Considering the following two usage scenarios (exactly as you see them, that is, the end-user will only be interested in using Vector2_t
and Vector3_t
):
[1]Inheritance:
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;
[2]Specialization:
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;
I can't make up my mind as to which is a nicer solution.
The obvious advantage to inheritance is code reuse in the derived classes; a possible disadvantage being performance (bigger size, users may pass by value, etc).
Specialization seems to avoid all that, but at the expense of me having to repeat myself multiple times.
What other advantages/disadvantages did I miss, and in your opinion, which route should I take?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为你最终想要的是拥有用户类型
并且根据
N
,用户会得到稍微不同的东西。 第一个无法实现这一点,但第二个可以,但代价是代码重复。您可以做的是反转继承:
并实现仅依赖于 N 是适当基类中的某个特定值的少数函数。 您可以在其中添加受保护的析构函数,以防止用户通过指向
VectorBase
的指针删除Vector
的实例(通常他们甚至不应该能够命名VectorBase
code>:将这些基础放在某个实现命名空间中,例如detail
)。另一个想法是将这个解决方案与另一个答案中提到的解决方案结合起来。 私有继承(而不是如上所述的公开继承)并将包装器函数添加到调用基类的实现的派生类中。
另一种想法是仅使用一个类,然后使用
enable_if
(使用boost::enable_if
)针对特定的N
值启用或禁用它们,或者使用像这样简单得多的 int 到类型转换器这样,它对
Vector
的用户是完全透明的。 它也不会为编译器进行空基类优化(很常见)增加任何空间开销。What you ultimately want, i think, is to have the user type
And depending on
N
, the user will get slight different things. The first will not fulfill that, but the second will, on the price of code duplication.What you can do is to invert the inheritance:
And implement the few functions that depend only on N being some specific value in the appropriate base-class. You may add a protected destructor into them, to prevent users deleting instances of
Vector
through pointers toVectorBase
(normally they should not even be able to nameVectorBase
: Put those bases in some implementation namespace, likedetail
).Another idea is to combine this solution with the one mentioned in another answer. Inherit privately (instead of publicly as above) and add wrapper functions into the derived class that call the implementations of the base-class.
Yet another idea is to use just one class and then
enable_if
(usingboost::enable_if
) to enable or disable them for particular values ofN
, or use a int-to-type transformer like this which is much simplierThat way, it is completely transparent to the user of
Vector
. It also won't add any space overhead for compilers doing the empty base class optimization (quite common).使用继承和私有继承。 并且不要使用任何虚函数。 由于使用私有继承,您没有 is-a,因此没有人能够使用指向派生子类的 baseclas 指针,并且在按值传递时不会遇到切片问题。
这为您提供了两全其美的好处(事实上,这就是大多数库实现许多 STL 类的方式)。
来自 http://www.hackcraft.net/cpp/templateInheritance/ (讨论 std: :vector,而不是您的 Vector 类):
一般来说,如果 STL 这样做,它似乎是一个不错的模拟模型。
Use inheritance and private inheritance. And don't use any virtual functions. Since with private inheritance, you don't have is-a, no one will be able to use a baseclas pointer to a derived subclass, and you won't get the slicing problem when passinfg by value.
This gives you the best of both worlds (and indeed it's how most libraries implement many of the STL classes).
From http://www.hackcraft.net/cpp/templateInheritance/ (discussing std::vector, not your Vector class):
In general, if the STL does it like this, it seems like a decent model to emulate.
继承只能用于建模“is-a”。 专业化将是更清洁的选择。 如果您出于某种原因需要或想要使用继承,至少将其设为私有或受保护的继承,这样您就不会从具有公共非虚拟析构函数的类公开继承。
是的,模板元编程人员总是这样做,
但那些
东西
是元函数,并不意味着用作类型。Inheritance should only be used to model "is-a". Specialization would be the cleaner alternative. If you need or want to use inheritance for whatever reason, at the very least make it private or protected inheritance, so you don't inherit publicly from a class with a public non-virtual destructor.
Yes, the template metaprogramming guys always do
But those
something
s are metafunctions and not meant to be used as types.您需要根据您的实际用例以及真正的公共接口应该是什么来确定以下问题的“正确”答案:
“
Vector”是您的主要接口吗?公共接口还是实现细节? 例如,人们是否应该使用这些对象,例如
std::vector
或者它只是隐藏“vector_2
”真实 API 的常见实现逻辑的一种方法,等等.?所有“
Vector
”是否具有相同的逻辑行为? 大多数人会告诉您在进行此类决策时遵循 STL,但 STL 本身却因std::vector
而臭名昭著地搞砸了这一点。 他们被迫支持由此造成的混乱局面 30 年。需要记住的一件事是,您可以将继承和模板特化结合起来; 它们并不相互排斥。 模板专业化应该对用户透明,这意味着它们应该具有与通用模板相同的公共接口和行为。
例如:
在通用“Vector”之间共享代码 及其专业,您有多种选择。 最直接的方法是将其放入基类的受保护函数中。
You need to decide the 'correct' answer to the following questions, based on your actual use case and what the real public interface should be:
Is "
Vector<T,N>
" your main public interface or an implementation detail? As in, are people supposed to use these objects likestd::vector
or is it simply a way to hide common implementation logic for the real API of "vector_2
", etc.?Will all "
Vector<T,N>
" have the same logical behavior? Most people will tell you to follow the STL when it comes to these types of decisions, but the STL itself infamously screwed this up withstd::vector<bool>
. And they've been forced to support the resulting mess for 30 years.One thing to bear in mind is that you can combine both inheritance and template specialization; they're not mutually exclusive. And template specializations should be transparent to users, meaning they should have the same public interface and behavior as the general template.
For example:
To share code between the generic "Vector<T,N>" and its specializations, you have several options. The most straightforward way is the put it in protected functions of the base class.
如果您过多地使用模板专业化,您可能需要重新考虑您的设计。 考虑到您将其隐藏在
typedef
后面,我怀疑您是否需要它。If you're using template specialization too much, you probably need to rethink your design. Considering that you're hiding it behind a
typedef
, I doubt you need it.