【问题标题】:Type inference in C++ operator overloadC++ 运算符重载中的类型推断
【发布时间】:2021-03-05 03:31:09
【问题描述】:

我是一名机械工程师,试图创建一个模拟库,其中包含物理类型(距离、力、力矩、加速度、速度等)以及一些矢量数学(模板 Vector3D)以支持这些类型。除了我想对运算符'^'应用重载的叉积之外,正常向量运算的一切都很顺利。叉积运算接受两种不同的类型并返回第三种类型,请参阅下面的示例列表。

Vector3D<Moment> = Vector3D<Distance> ^ Vector3D<Force>;
Vector3D<Acceleration> = Vector3D<AngularAcceleration> ^ Vector3D<Velocity>;
Vector3D<Velocity> = Vector3D<AngularVelocity> ^ Vector3D<Distance> etc. etc.

现在理想情况下,我不想根据返回类型和输入参数为“^”操作数重载设置不同的方法。现在我知道下面的代码不起作用,因为您不能使用三个不同的模板进行经典运算符重载,编译器将不知道该怎么做。

template <typename T, typename U, typename V>
Vector3D<T> operator^ (const Vector3D<U>& p_left, const Vector3D<V>& p_right)

果然我得到了以下编译错误 C2593: 'operator ^' is ambiguous。我想知道是否有一些类型推断和/或实例化可以在这里很好地使用,尽管我已经在网上做了一些阅读,但似乎没有什么符合手头的具体要求。我认为我需要构造函数来转换类型,但这本身是不够的

/// Constructor
/// \param p_value Moment arm
/// \param p_unit Force
//
Moment(Distance p_radius, Force p_force);

/// Constructor
/// \param p_omega      Magnitude of angular velocity
/// \param p_velocity   Magnitude of linear velocity
//
Acceleration(AngularVelocity p_omega, Velocity p_velocity);

/// Constructor
/// \param p_value  Angular velocity
/// \param p_unit   Radius
Velocity(AngularVelocity p_omega, Distance p_radius);

您在这件事上的帮助将对我有很大帮助,因为编程仍然是我不断学习的领域...请参阅下面有关我的类 Vector3D 的更多信息。提前谢谢!

//Vector3D.h
template <typename T>
class Vector3D
{
public:
    //////////////////////////////////////////////////////////////////////////
    // *** CONSTRUCTORS ***
    //////////////////////////////////////////////////////////////////////////

    /// Default Constructor
    //
    Vector3D(void);

    /// External initialization Constructor
    //
    Vector3D(const T& p_x, const T& p_y, const T& p_z);

    //////////////////////////////////////////////////////////////////////////
    // *** DESTRUCTOR ***
    //////////////////////////////////////////////////////////////////////////

    /// Destructor
    //
    ~Vector3D(void);

    template <typename U, typename V>
    friend Vector3D<T> operator^ (const Vector3D<U>& lhs, const Vector3D<V>& rhs);
'''
private:
//////////////////////////////////////////////////////////////////////////
// *** PRIVATE DATA MEMBERS ***
//////////////////////////////////////////////////////////////////////////
T m_elem[3];

}

//Vector3D.cpp
/// Calculate the cross product
//
template <typename T, typename U, typename V>
Vector3D<T> operator^ (const Vector3D<U>& p_left, const Vector3D<V>& p_right)
{
    Vector3D<T>cross_product;
    // Calculation
    cross_product.m_elem[0] = T(p_left.m_elem[1], p_right.m_elem[2]) - T(p_left.m_elem[2], p_right.m_elem[1]);
    cross_product.m_elem[1] = T(p_left.m_elem[2], p_right.m_elem[0]) - T(p_left.m_elem[0], p_right.m_elem[2]);
    cross_product.m_elem[2] = T(p_left.m_elem[0], p_right.m_elem[1]) - T(p_left.m_elem[1], p_right.m_elem[0]);

    return cross_product;
}

template class Vector3D<double>;
template class Vector3D<double>;
template class Vector3D<Numeric>;

template class Vector3D<Force>;
template class Vector3D<Acceleration>;
template class Vector3D<Velocity>;
template class Vector3D<Distance>;

template class Vector3D<Moment>;
template class Vector3D<AngularAcceleration>;
template class Vector3D<AngularVelocity>;
template class Vector3D<Angle>;

【问题讨论】:

  • 您的单位是否已经有operator* 过载?如果Distance * Force 已经返回了Moment,我认为应该有办法从decltype 中取出U 和V 的正确返回值。
  • //Vector3D.cpp:模板必须是implemented in headers entirely(少数特殊例外)。
  • @Aconcagua 看起来Vector3D.cpp 正在从该问题的第一个答案中实现“显式实例化您需要的所有模板实例”方法。
  • @NathanPierson 部分。不过,operator^ 没有明确的实例化。
  • 也许首先让有维度的标量数学工作。有一些图书馆可以做到这一点,例如boost.units。在这种情况下,像Moment(Distance p_radius, Force p_force) 这样的构造函数并不是非常有用。顺便说一句,它被称为“动量”。

标签: c++ templates operator-overloading type-inference


【解决方案1】:

主要问题是:叉积算子的实现中T的类型是什么?这就是产生歧义的原因。

首先,您可能希望用乘法运算符而不是构造函数替换构造函数,即

Moment operator*(Distance p_radius, Force p_force);

而不是

Moment(Distance p_radius, Force p_force);

这允许您从尾随返回类型中的模板参数推断返回类型。您的运算符可能如下所示:

template<typename U, typename V>
auto operator^(const Vector3D<U>& left, const Vector3D<V>& right)
    -> Vector3D<decltype(std::declval<U>() * std::declval<V>())>
{
    return
    {
        left.m_elem[1] * right.m_elem[2] - left.m_elem[2] * right.m_elem[1],
        left.m_elem[2] * right.m_elem[0] - left.m_elem[0] * right.m_elem[2],
        left.m_elem[0] * right.m_elem[1] - left.m_elem[1] * right.m_elem[0]
    };
}

【讨论】:

  • 嘿@Aconcagua,当我实施您的方法时,一切正常。但是我失去了做简单代码数学的能力,例如 Vector3D = Vector3D ^ Vector3D 这是我唯一需要支持的东西,除了我提到的标称类型(力、力矩、距离等)。即使我支持 Vector3D operator^ (const Vector3D& lhs, const Vector3D& rhs) 试图避免链接错误,链接器似乎总是选择 operator^ 重载相关的 decltype 并引发错误。你知道如何着手解决这个差异吗?
  • @hdamlaj 编译器和确切的错误消息? GCC 编译得很好,请参阅 [这里](godbolt.org/z/WY669Y)。由于您在初始版本中已经减去了类型,我假设您已经定义了自定义 operator-?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-15
  • 1970-01-01
  • 2010-10-19
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 2019-11-17
相关资源
最近更新 更多