我已经实现了自己的SI Unit类。使用算术运算时,生成的SI单位可以更改。例如:(米/秒)/米= 1 /秒。
好的,现在我还创建了一个简单的3D Vector类。该向量应是通用的,也可用于我的SI单元类。所以我实现了一个简单的除法运算符 请参阅以下代码:
// 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
到目前为止一切顺利。问题是当我定义第二个运算符(Vector / Scalar)时,这个运算符是如此通用,以至于编译器也想将它用于向量/向量除法。 但它失败了,因为Lhs()/ Rhs()带有:
Lhs = Tools :: DataTypes :: Length and Rhs = RobotTools :: DataTypes :: TV3Type
未定义。这是正确的,我理解给定的错误。我不明白的是编译器不使用Vector / Vector运算符。
答案 0 :(得分:2)
编译器不希望将Vector / Scalar运算符用于向量/向量除法。它只是想检查一下是否匹配。
如果声明(但未定义)完全通用的除法运算符
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/
。编译器推断出Lhs
和Rhs
模板参数都是Length
(为简洁起见,我省略了名称空间)。然后,它将它们替换为TV3TypeV3Div
的参数,计算TV3TypeV3Div
的内部,确定TV3TypeV3Div<Lhs,Rhs>::type
也是Length
,最后计算operator/
的返回类型这是TV3Type<Length>
。
现在考虑Vector / Scalar operator/
。编译器推断Lhs
为Length
,但Rhs
为TV3Type<Length>
。然后,它将这些类型替换为TV3TypeV3Div
的参数,并尝试计算TV3TypeV3Div
的内部。这是事情的分歧:没有operator/
可以接受Length
和TV3Type<Length>
。因此无法计算TV3TypeV3Div<Lhs,Rhs>::type
。编译器输出错误。
现在考虑声明通用operator/
时会发生什么。现在, operator/
即可接受Length
和TV3Type<Length>
! (或者任何其他一对论点,就此而言)。因此编译器愉快地计算TV3TypeV3Div<Lhs,Rhs>::type
,然后返回Vector / Scalar operator/
的返回类型。所以现在可以考虑Vector / Vector和Vector / Scalar operator/
重载。
编译器现在也会看到并考虑通用operator/
进行重载解析。所有三个重载产生匹配。但Vector / Vector重载获胜,因为它比Vector / Scalar或泛型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
之外,这应该可以很好地隐藏通用运算符/。