2

我已经实现了自己的 SI Unit 类。使用算术运算时,生成的 SI 单位可能会发生变化。例如:(米/秒)/米=1/秒。

好的,现在我还创建了一个简单的 3D Vector 类。这个向量应该是通用的,也可以用于我的 SI Unit 类。所以我实现了一个简单的除法运算符。请参阅以下代码:

// Determine result type of Lhs / Rhs:
template < class Lhs, class Rhs >
    struct TV3TypeV3Div { typedef BOOST_TYPEOF( Lhs( ) / Rhs( ) ) type; };

// Vector / Vector:
template < class Lhs, class Rhs >
RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type > operator/( const RobotTools::DataTypes::TV3Type< Lhs >& lhs,
                                                                                     const RobotTools::DataTypes::TV3Type< Rhs >& rhs )
{
    // do something useful
    return RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type >( 0, 0, 0 );
}

// Vector / Vector
RobotTools::DataTypes::TV3Type< Tools::DataTypes::Length > vl;
vl / vl;  // Ok this works

在编译期间,正确的返回类型将通过使用 TV3TypeV3Div 结构来确定。这行得通。

现在我想扩展运营商。我还想计算标量类型的向量。所以我写了这个运算符:

// Vector / Scalar
template < class Lhs, class Rhs >
RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type > operator/( const RobotTools::DataTypes::TV3Type< Lhs >& lhs,
                                                                                     const Rhs& rhs )
{
    // do something useful
    return RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Tools::DataTypes::Length >::type >( 0, 0, 0 );
}

// Vector / Scalar
RobotTools::DataTypes::TV3Type< Tools::DataTypes::Length > vl;
Tools::DataTypes::Length sl;
vl / sl;  // Ok nice it works too

到现在为止还挺好。问题是当我定义第二个运算符(向量/标量)时,这个运算符非常通用,编译器也希望将它用于向量/向量除法。但它失败了,因为 Lhs( ) / Rhs( ) 有:

Lhs=Tools::DataTypes::Length 和 Rhs=RobotTools::DataTypes::TV3Type

没有定义。这是正确的,我理解给定的错误。我不明白的是编译器不使用 Vector/Vector 运算符。

  • 有没有可能给编译器提示使用哪个运算符?
  • 有没有可能重写运算符来满足我的要求?
4

1 回答 1

2

编译器不想使用向量/标量运算符进行向量/向量除法。它只是想检查是否有匹配。

如果您声明(但未定义)一个完全通用的除法运算符

template <typename Lhs, typename Rhs>
struct InvalidDivision {};
template <typename Lhs, typename Rhs>
InvalidDivision<Lhs, Rhs> 
operator/(const Lhs& lhs, const Rhs& rhs); // do not define

那么你的代码应该编译和链接。Vector/Scalar 重载将被考虑并拒绝,因为 Vector/Vector 是更好的匹配。

它的缺点是,如果你对没有定义除法的东西进行除法,编译器会抱怨InvalidDivision<something,other>而不是给出通常的错误。

也许这可以通过使用 SFINAE 或其他一些高级魔法来改善。

更新:更详细的解释

原始版本的代码发生了什么?

我们正在尝试为用户定义的类型调用除法运算符。有两个函数被命名operator/,编译器会同时考虑这两个函数并试图找出哪一个更匹配。

首先考虑 Vector/Vector operator/。编译器推断出模板参数LhsRhs模板参数都是Length(为简洁起见,我省略了命名空间)。然后将它们代入 的参数TV3TypeV3Div,计算 的内部TV3TypeV3Div,确定TV3TypeV3Div<Lhs,Rhs>::typeis Length,最后计算operator/is的返回类型TV3Type<Length>

现在考虑 Vector/Scalar operator/。编译器推断出Lhsis Length,但是Rhsis TV3Type<Length>。然后它将这些类型替换为 的参数TV3TypeV3Div,并尝试计算 的内部TV3TypeV3Div。这就是事情破裂的地方:没有operator/可以接受LengthTV3Type<Length>. 所以无法计算TV3TypeV3Div<Lhs,Rhs>::type。编译器输出错误。

现在考虑声明泛型时会发生什么operator/。现在有一个可以operator/接受LengthTV3Type<Length>!(或任何其他一对论点,就此而言)。所以编译器会愉快地计算TV3TypeV3Div<Lhs,Rhs>::type然后返回 Vector/Scalar 的类型operator/。所以现在可以同时考虑 Vector/Vector 和 Vector/Scalaroperator/重载。

编译器现在还看到并考虑operator/重载决议的泛型。所有三个重载都会产生匹配。但是 Vector/Vector 重载获胜,因为它比 Vector/Scalar 或 generic 更专业operator/,因此是更好的匹配。

更新 2

应该可以这样做:

namespace Detail
{
    template <typename Lhs, typename Rhs>
        struct DoNotUse {};
    template <typename Lhs, typename Rhs>
        DoNotUse<Lhs, Rhs> operator/(const Lhs& lhs, const Rhs& rhs);
    template < class Lhs, class Rhs >
        struct TV3TypeV3Div { typedef BOOST_TYPEOF( Lhs( ) / Rhs( ) ) type; };
}
using Detail::TV3TypeV3Div;

这应该很好地向所有人隐藏通用运算符/,但TV3TypeV3Div.

于 2012-07-01T12:55:47.027 回答