Pan*_*sis 4 c++ templates design-patterns dynamic-cast this
这更多的是一个设计问题。
我有一个模板类,我想根据模板类型向其添加额外的方法。为了实践 DRY 原则,我提出了这个模式(有意省略定义):
template <class T>
class BaseVector: public boost::array<T, 3>
{
protected:
BaseVector<T>(const T x, const T y, const T z);
public:
bool operator == (const Vector<T> &other) const;
Vector<T> operator + (const Vector<T> &other) const;
Vector<T> operator - (const Vector<T> &other) const;
Vector<T> &operator += (const Vector<T> &other)
{
(*this)[0] += other[0];
(*this)[1] += other[1];
(*this)[2] += other[2];
return *dynamic_cast<Vector<T> * const>(this);
}
virtual ~BaseVector<T>()
{
}
}
template <class T>
class Vector : public BaseVector<T>
{
public:
Vector<T>(const T x, const T y, const T z)
: BaseVector<T>(x, y, z)
{
}
};
template <>
class Vector<double> : public BaseVector<double>
{
public:
Vector<double>(const double x, const double y, const double z);
Vector<double>(const Vector<int> &other);
double norm() const;
};
Run Code Online (Sandbox Code Playgroud)
我希望 BaseVector 只是一个实现细节。这有效,但我担心operator+=。我的问题是:指针的动态转换是否有this代码味道?有没有更好的方法来实现我想要做的事情(避免代码重复和用户代码中不必要的转换)?或者我安全吗,因为 BaseVector 构造函数是私有的?
抱歉,是的,我有虚拟 dtor,但我忘记粘贴它,没有它,代码无法编译。
我建议您考虑一种替代方法(请注意,在下面的示例中,我已将代码简化到演示替代方法所需的最低限度)。
首先,考虑奇怪的重复模板参数(CRTP):
template <typename T, typename DerivedVector>
struct BaseVector
{
DerivedVector& operator+=(DerivedVector const& other)
{
return static_cast<DerivedVector&>(*this);
}
};
template <typename T>
struct Vector : BaseVector<T, Vector<T>>
{
};
Run Code Online (Sandbox Code Playgroud)
由于您始终知道派生类型是什么,因此 astatic_cast就足够了。如果Vector<T>是唯一的基类,BaseVector<T>并且如果保证T参数始终相同,那么严格来说,CRTP 参数是不必要的:您始终知道派生类型是什么,因此 astatic_cast是安全的。
或者,考虑运算符不必是成员函数,因此您可以声明非成员运算符函数模板:
template <typename T, typename DerivedVector>
struct BaseVector
{
};
template <typename T>
struct Vector : BaseVector<T, Vector<T>>
{
};
template <typename T>
Vector<T>& operator+=(Vector<T>& self, Vector<T> const& other)
{
return self;
}
Run Code Online (Sandbox Code Playgroud)
虽然后者更适合操作员,但它不适用于普通的非操作员成员函数,因此 CRTP 方法更适合这些操作员。