2015-06-10 45 views
5

考慮到與轉換函數以下用戶定義類型S將非算術類型作爲參數傳遞給cmath函數是否有效?

#include <cmath> 

void test(S s) { 
    std::sqrt(s); 
    std::log(s); 
    std::isgreater(s,1.0); 
    std::isless(s,1.0); 
    std::isfinite(s) ; 
} 

此代碼編譯與gcc

struct S 
{ 
    operator double() { return 1.0;} 
}; 

和使用類型S下列調用cmath功能使用libstdc++see it live)但使用clang使用libc++它生成若干呼叫(see it live)與以下錯誤的錯誤爲isgreater

error: no matching function for call to 'isgreater' 
    std::isgreater(s,1.0); 
    ^~~~~~~~~~~~~~ 

note: candidate template ignored: disabled by 'enable_if' [with _A1 = S, _A2 = double] 
std::is_arithmetic<_A1>::value && 
^ 
islessisfinite

和類似的錯誤,所以libc++預計對那些呼叫的參數爲arithmetic types其中S是不,我們可以通過去libc++ cmath header的來源來確認。儘管在libc++的所有cmath函數中對算術類型的要求不一致。

所以問題是,將非算術類型作爲參數傳遞給cmath函數是否有效?

回答

7

TL; DR

根據它是有效的傳遞非算術類型作爲參數傳遞給cmath功能,但缺陷報告2068標準認爲原意是cmath功能應該被限制到算術類型和看起來可能使用非算術參數最終會形成不合格。因此,儘管技術上使用非算術類型作爲參數有效,但根據缺陷報告2068來看似乎有問題。

詳細

cmath頭被覆蓋在標準牽伸部26.8[c.math]提供了額外的浮子長雙過載,取入math.h定義的每個功能一個的說法,並進一步,段落11提供了足夠的過載和說:

此外,存在應足以確保附加重載:

  1. 如果對應於一個雙參數的任何參數的類型爲長雙,則對應於雙參數的所有參數都 有效轉換爲長一倍。
  2. 否則,如果任何對應於雙參數的參數具有double型或整數型,則所有與參數 雙參數相對應的參數將被有效地轉換爲double。
  3. 否則,與雙參數相對應的所有參數都將有效地轉換爲浮點型。

這C++ 11

似乎有效在C++ 11部分26.8[c.math]不包括禁止非算術參數cmath功能的任何限制。在每個案例中,我們都有一個超載可用,需要double參數,這些參數應該通過overload resolution選擇。

缺陷報告2086

但對於C++ 14我們有defect report 2086: Overly generic type support for math functions,這認爲部分26.8[c.math]的原意是限制cmath功能只爲有效算術類型,這會模仿他們如何在ç工作:

我的感覺是,這個規則集可能是多個G通用爲 打算,我的假設是,它被寫成模仿在C++/C++中799 p2 + 3規則集中的C99/C1x 規則集(請注意C約束 有效設置爲類型C++描述爲算術類型,但看到 下面的一個重要的區別)[...]

,並說:

我目前的建議,以解決這些問題將是約束 有效的參數類型這些函數的算術類型。

和改寫部分26.811說(重點煤礦):

此外,應再重載足以保證:

  1. 如果任何算術說法對應一個雙參數有類型long double,則全部算術參數對應的 雙參數被有效地強制轉換爲長雙。
  2. 否則,如果對應於一個雙參數的任何算術參數的類型是雙或整數類型,則對應於雙參數所有算術 參數被有效地轉換爲 兩倍。
  3. 否則,對應雙參數所有算術參數被有效轉換爲有效地轉換爲有型浮動。

所以這在C++ 14中是無效的?

好,儘管它看起來技術上仍然有效,認爲從討論此評論中libc++ bug report: incorrect implementation of isnan and similar functions意圖:

這可能是意圖,但我沒有看到任何方式閱讀 標準的措辭。從例子中評論#0:

std::isnan(A()); 

有沒有算術類型的參數,所以沒有子彈的 26.8/11適用。重載集合包含'isnan(float)','isnan(double)'和'isnan(long double)',並且'isnan(float)'應該被選擇爲 。

因此,通過一段的DR 2086的措詞並不能使病態的調用浮動長雙與非算術參數可用,否則超負荷。

技術上有效,但可疑的使用

因此,儘管C++ 11和C++ 14標準不限制cmath功能算術參數DR 2068認爲26.811的目的是爲了限制cmath功能只採用算術參數,顯然是爲了彌補C++ 14中的漏洞,但沒有提供足夠強的限制。

依賴可能在標準的未來版本中變形的特徵似乎值得懷疑。由於我們有實現差異,任何依賴於非函數參數傳遞給這些函數的代碼都是不可移植的,因此僅在有限的情況下才有用。我們有一個替代的解決方案,這是明確的投非算術類型算術類型,它繞過了整個問題,我們再也不用擔心代碼成爲形成不良的,它是便攜式:

std::isgreater(static_cast<double>(s) ,1.0) 
       ^^^^^^^^^^^^^^^^^^^^^^ 

作爲Potatoswatter指出使用一元+也是一種選擇:

std::isgreater(+s ,1.0) 

更新

由於TC在C++ 11它可以認爲26.811段子彈3適用由於參數是既不長雙也不是整數,因此應S類型的參數應該被投射到浮子首先指出。請注意,如缺陷報告gcc所示,從未執行過此操作,據我所知clang也沒有。

+0

可以說,C++ 11的寫法要求將's'轉換爲'float',因爲它的類型既不是'long double'也不是'double',也不是整數類型。 –

+0

@ T.C。我一直在辯論那個,但是是有爭議的。請注意,'libC++'和'libstdC++'都不以這種方式實現。 –

+0

'+ s'也被認爲是慣用的。 – Potatoswatter

相關問題