2012-07-01 178 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

相關問題