是否可以将动态强制转换"this"作为返回值?
Is it ok to dynamic cast "this" as a return value?
这更像是一个设计问题。
我有一个模板类,我想根据模板类型向其添加额外的方法。为了实践 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;
};
我打算让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>>
{
};
由于您始终知道派生类型是什么,因此static_cast
就足够了。 如果 Vector<T>
是唯一一个基数为 BaseVector<T>
的类,并且如果您保证T
参数始终相同,那么严格来说,CRTP 参数是不必要的:您始终知道派生类型是什么,因此static_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;
}
虽然后者对于运算符更可取,但它不适用于普通的非运算符成员函数,因此 CRTP 方法更适合这些函数。
是的,从技术角度来看,这是可以的。但是,为了使dynamic_cast
正常工作,基类必须是多态的。所以你至少需要使析构函数(纯)虚拟。
我还想添加它,而不是:
// potential null dereference
return *dynamic_cast<Vector<T> * const>(this);
写起来更安全:
// potential std::bad_cast exception
return dynamic_cast<Vector<T> & const>(*this);
要回答您的原始问题:
有没有更好的方法来实现我正在尝试做的事情(避免代码重复和用户代码中不必要的强制转换)?
是的。如果您想了解更多信息,请阅读静态多态性、奇怪的重复模板模式和基于策略的类设计。
您的所有方法都不virtual
.如果没有任何虚拟方法,编译器将不会添加任何运行时类型信息。没有RTTI,dynamic_cast
将无法工作。
这一点的更好方法是痘痘成语。 正如你所说,BaseVector只是一个实现细节。 因此,您类的客户端应该不知道它(这使您可以自由更改它)。
在这种情况下,这将需要Vector
包含 BaseVector,而不是从它继承。 然后,它将定义自己的算术赋值运算符,这些运算符将转发给BaseVector。
这并不违反 DRY,因为仍然只有一个版本的实现细节,并且它们驻留在 BaseVector
. 重复界面是完全可以的。
这是一个老问题,但我最近不得不解决同样的问题,然后碰巧偶然发现了这个问题,所以我想我会回答。
我使用了一个类型包装器,以便使专业化"派生自自身":
template <class T>
struct type_wrapper;
template <class T>
struct unwrap_type
{
typedef T type;
};
template <class T>
struct unwrap_type<type_wrapper<T>>
{
typedef T type;
};
template <class WrappedT>
class Vector: public boost::array<typename unwrap_type<WrappedT>::type, 3>
{
typedef typename unwrap_type<WrappedT>::type T;
protected:
Vector(const T x, const T y, const T z);
public:
bool operator == (const Vector &other) const;
Vector<T> operator + (const Vector &other) const;
Vector<T> operator - (const Vector &other) const;
Vector<T> &operator += (const Vector &other)
{
(*this)[0] += other[0];
(*this)[1] += other[1];
(*this)[2] += other[2];
return static_cast<Vector<T> &>(*this);
}
}
template <>
class Vector<double> : public Vector<type_wrapper<double>>
{
public:
Vector(const double x, const double y, const double z);
Vector(const Vector<int> &other);
double norm() const;
};
这样,您就不需要任何冗余类 - 除了可重用的类型包装器和元函数。
- 从python中调用C++函数并获取返回值
- 为什么模板类中的对象不能返回值
- 返回值优化:显式移动还是隐式
- lock_guard是否保护返回值
- 调用CreateProcess()并获取字符串的返回值
- 如何使 windows 命令提示符在C++可执行文件上显示返回值?
- 编译器警告:执行到达值返回函数的末尾而不返回值
- 查找 GCD:并非所有控制路径都返回值
- 在 Arduino 上使用 sscanf 会导致与 const char * 不匹配,并且返回值始终相同,尽管输入值不同
- 将返回值存储在函数指针数组的指针中是如何工作的?
- 如何从 std::thread 返回值
- 将返回值从 exe 传递到 bat,并将其传递给 C# 中的进程
- 方法错误"not all control paths return a value"和方法不返回值
- 如何读取 C++ SAFEARRAY**,该 SAFEARRAY** 是 COM 互操作的结果,其中 C# 返回值为
- 对fread的返回值感到困惑
- 程序不向函数返回值
- 如何在另一个函数中使用返回值作为参数?
- 如何使用 uint64_t 键类型从 std::map<int, std::string> 返回值?
- 是否可以将动态强制转换"this"作为返回值?
- 使用 *this 作为返回值