可以动态转换“this”作为返回值吗?

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,但我忘记粘贴它,没有它,代码无法编译。

Jam*_*lis 5

我建议您考虑一种替代方法(请注意,在下面的示例中,我已将代码简化到演示替代方法所需的最低限度)。

首先,考虑奇怪的重复模板参数(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 方法更适合这些操作员。

  • 你是对的。提供正确模板参数的负担仍然由派生类承担。您可以提供一些验证,例如,通过保护所有基类构造函数并让每个构造函数采用“DerivedVector*”作为参数。派生类必须传递“this”。当然,邪恶的派生类可以传递 nullptr 或另一个“DerivedVector*”,但无法防止有人决心破坏事物。 (2认同)