2013-01-12 19 views
9

讓我們以下面的例子程序:不正確的重載

#include <cmath> 

namespace half_float 
{ 
    template<typename T> struct half_expr {}; 

    struct half : half_expr<half> 
    { 
     operator float() const; 
    }; 

    template<typename T> half sin(const half_expr<T>&); 
    template<typename T> half atan2(const half_expr<T>&, const half_expr<T>&); 
} 

using namespace std; 
using half_float::half; 

int main() 
{ 
    half a, b; 
    half s = sin(a); 
    half t = atan2(a, b); 
} 

VS 2010這個編譯就好(忽略了明顯的鏈接錯誤,現在)。但在VS 2012這給了我:

錯誤C2440: '轉換':不能從 '浮動' 轉換爲 'half_float ::一半'

如此看來重載解析沒有按」 t從名稱空間half_float(ADL應該完成)中挑選版本,但從std使用隱式轉換爲float的版本。但奇怪的是,這隻發生在atan2呼叫而不是sin呼叫。

在較大的項目中,實際上首次發生此錯誤時,對於其他雙參數函數(或具有2 half參數的參數)也會出現此錯誤,如fmod,但不適用於任何單參數函數。同樣在較大的項目中,它也可以正常工作gcc 4.6/4.7鏗鏘3.1沒有錯誤,雖然我沒有明確測試這個SSCCE版本。

所以我的問題是,是VS 2012身邊這個錯誤的行爲(因爲它僅發生於和僅2個參數的功能),還是我負責一些細微之處的過載分辨率規則(我猜可能確實會有點棘手)?

編輯:它還如果我直接using namespace half_float或者直接把整個事情的全局命名空間中發生的。同樣,如果我不是using namespace std,也會發生這種情況,但這是VS-實現將數學函數放入全局名稱空間。

編輯:它與原來的VC 2012編譯器中,都有發生,以及在2012年11月CTP它。

編輯:雖然我不能完全肯定這是真的違反了嚴格意義上的標準,我已經申請根據我的回答結果爲它bug,因爲它至少是不一致1參數函數的定義,並值得VS -Team進一步調查。

+2

VS2012似乎有錯誤。 – Nawaz

+1

我同意Nawaz。這在我的Apple LLVM 4.1工具鏈中沒有問題地編譯出來。它也適用於VS2010有點說明問題。你用CTP試過了嗎? – WhozCraig

+0

@WhozCraig不,好主意,很快就會嘗試。 –

回答

6

我想我找到了原因。 C++標準說,在部分26.8 [c.math],對於C庫的數學函數,

,須有足夠,以保證附加重載:

  1. 如果任何參數對應給double類型的參數賦予double類型的double型參數,然後所有參數對應的double參數都是 ,有效投射爲long double。
  2. 否則,如果任何對應於double參數的參數具有double型或整數型,則對應於雙參數的所有參數將被有效地轉換爲double。
  3. 否則,與雙參數相對應的所有參數都將有效地轉換爲浮點型。

這也可以在atan2 documentation中看到。

template<typename T,typename U> common_float_type<T,U>::type atan2(T, U); 

所以我們有一個模板函數,它的實例會涉及的隱式轉換(從half&const half_expr<half>&

這些重載由2012VS通過使用形式的一般功能模板提供)和一個可以直接實例化的模板函數。因此後者是優選的。這對於單參數函數不會發生,因爲對於那些只需要用於積分參數的通用版本,VS 2012僅適用於使用std::is_integralstd::enable_if的整數參數。

但我認爲這個標準有點不清楚,那些「附加過載」只適用於內置類型。所以最後我還是不確定是否嚴格違反了標準的過度通用功能,或者如果它是一個可行的實現選項來提供這些標準。

編輯:,因爲它似乎,已經有defect report 2086爲標準的不明確的措辭和修復是它的方式,限制了這些額外的重載只算術類型的要求。由於這似乎一直是最初的意圖(並且幾乎所有現有的實現都已經實現),而且這只是措辭不清楚,所以我確實認爲這是一個在實現中的bug。

0

我剛剛試過你的代碼,我發現它有什麼問題。

由於您尚未實施half::sinhalf::atan2,因此鏈接器將會拋出錯誤。所以如果你實施方法half::sinhalf::atan2,那就應該解決它(我通過讓它們返回空的一半來實現它們,當然這是毫無意義的)。

當我採取了這一步(提供了一個(無意義的)兩種必需方法的實現)之後,錯誤信息幾乎神奇地消失了。

也許這不是你的問題的解決方案,因爲我使用GCC,而不是VS.


編輯:我只是想我以G ++使用與Visual Studio樣品,這給了我peculier錯誤消息。提供了錯誤的奇怪性,以及使用GCC的代碼,我必須得出結論,這是VC2012中的一個錯誤。

+0

不,就像在問題中寫的一樣,你得到的僅僅是一個鏈接器錯誤(因爲,正如你意識到的,它們沒有正確實現)。但是,使用VS2012你不會得到那麼多,並且你會得到我的問題中描述的編譯器錯誤。 –

+0

@ChristianRau我指導你到最新的編輯。 – antonijn

0

一種解決方法是專門爲_Common_float_typehalfhalf_expr是一個未定義的類型,從而使SFINAE擺脫的atan2的VS2012版本。

namespace std { 
    template<class T1, class T2> 
    struct _Common_float_type<half_float::half_expr<T1>, half_float::half_expr<T2>>; 
    template<class T2> 
    struct _Common_float_type<half_float::half, half_float::half_expr<T2>>; 
    template<class T1> 
    struct _Common_float_type<half_float::half_expr<T1>, half_float::half>; 
    template<> 
    struct _Common_float_type<half_float::half, half_float::half>; 
} 

請注意,你必須專注爲halfhalf_expr所有四種組合,因爲模板特不考慮基類。