2012-07-01 137 views
2

我已经实现了我自己的SI单元类。使用算术运算时,生成的SI单元可以更改。例如:(米/秒)/米= 1 /秒。取决于模板参数的C++模板返回类型?

好吧,我现在也创建了一个简单的3D矢量类。这个向量应该是通用的,并且可以与我的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/Vector除法。 但由于LHS()/右轴()与它失败:未定义

LHS =工具::数据类型::长度和rhs = RobotTools ::数据类型:: TV3Type

。这是正确的,我理解给定的错误。我不明白的是编译器不使用Vector/Vector操作符。

  • 有没有可能给编译器一个提示哪个运算符使用?
  • 有没有可能改写操作员以满足我的要求?

回答

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/,编译器会考虑并试图找出哪一个更好匹配。

首先考虑矢量/矢量operator/。编译器推断LhsRhs模板参数都是Length(为简洁起见,我省略了名称空间)。然后将它们替换为参数TV3TypeV3Div,计算TV3TypeV3Div的内部,确定TV3TypeV3Div<Lhs,Rhs>::type也是Length,最后计算返回类型operator/,即TV3Type<Length>

现在考虑矢量/标量operator/。编译器推断LhsLength,但RhsTV3Type<Length>。然后它将这些类型替换为参数TV3TypeV3Div,并尝试计算TV3TypeV3Div的内部。这是事情分手的地方:有没有operator/可以接受LengthTV3Type<Length>。所以不可能计算TV3TypeV3Div<Lhs,Rhs>::type。编译器输出一个错误。

现在考虑声明通用operator/时会发生什么情况。现在operator/,它会接受LengthTV3Type<Length>! (或者任何其他一对争论,就此而言)。所以编译器很高兴地计算出TV3TypeV3Div<Lhs,Rhs>::type,然后返回Vector/Scalar operator/的返回类型。所以现在可以考虑Vector/Vector和Vector/Scalar operator/重载。

编译器现在还会看到并考虑重载分辨率的通用operator/。所有三种重载都会产生匹配。但矢量/矢量超载赢了,因为它更专业化,无论是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

+0

我很困惑!这真的有用。我花了整整一天的时间为这个简单的解决方案:)你能解释一下多一点。我没有看到魔法。 – Mark

+0

我已经添加了一些解释。 –

+0

谢谢。为了很好的解释。与此同时,我在我原来的项目中尝试了你的方法。但它在多个地方出现“InvalidDivision ...”错误代码失败。我认为更新整个项目并不那么容易。所以也许我需要一些SFINAE魔术,也许通过使用提升?有任何想法吗 ? – Mark

相关问题