将非算术类型作为参数传递给cmath函数是否有效?

时间:2015-06-10 20:18:16

标签: c++ c++11 language-lawyer c++14 cmath

给定以下用户定义类型S,其转换函数为 double

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

以及使用S类型的<{3}}函数调用以下函数:

#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 cmath )与libstdc++进行编译,但clang使用libc++时会产生错误调用( see it live ),see it live出现以下错误:

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 &&
^

以及isgreaterisless的类似错误,因此libc++期望这些调用的参数为isfiniteS不是,我们可以确认通过转到arithmetic types的来源。虽然,cmath中所有libc++函数对算术类型的要求并不一致。

所以问题是,将非算术类型作为参数传递给cmath函数是否有效?

1 个答案:

答案 0 :(得分:7)

TL; DR

根据标准,将非算术类型作为参数传递给cmath函数是有效的,但缺陷报告2068认为原始意图是cmath函数应限于算术类型和看起来可能使用非算术参数最终会变得格格不入。因此,尽管使用非算术类型作为参数在技术上有效,但鉴于缺陷报告2068,这似乎是有问题的。

详细

草稿标准部分26.8 [c.math] 中提供了cmath标题,提供了额外的 float long math.h中定义的每个函数的双重载,它带有 double 参数,而11段提供了足够的重载和表示:

  

此外,还应有足够的额外过载来确保:

     
      
  1. 如果对应于double参数的任何参数的类型为long double,则所有与double参数对应的参数都是   有效地施展到长双。
  2.   
  3. 否则,如果对应于double参数的任何参数具有double类型或整数类型,则所有参数都对应于   双参数有效地加倍。
  4.   
  5. 否则,所有与double参数对应的参数都被有效地转换为float。
  6.   

这似乎在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函数仅对算术类型有效,这将模仿它们在 C 中的工作方式:

  

我的印象是这个规则集可能更通用了   我打算用它来模仿C99 / C1x   规则集在7.25 p2 + 3中的&#34; C ++&#34;方式[...](注意C约束   C ++描述为算术类型的类型的有效集,但请参阅   以下是一个重要的区别)[...]

并说:

  

我目前关于解决这些问题的建议是限制   这些函数的有效参数类型为算术类型。

并重新编写了26.811段(强调我的):

  

此外,还应有足够的额外过载来确保:

     
      
  1. 如果对应于double参数的任何算术参数的类型为long double,则所有算术参数对应于   双参数有效地转换成长双。
  2.   
  3. 否则,如果对应于double参数的任何算术参数具有double类型或整数类型,则所有算术   对应于double参数的参数被有效地转换为   双
  4.   
  5. 否则,对应于double参数的所有算术参数都被有效地强制转换为 有效地转换为具有类型 float。
  6.   

所以这在C ++ 14中无效?

好吧,尽管从libc++ bug report: incorrect implementation of isnan and similar functions的讨论中这个评论中所论述的意图看起来技术上仍然有效:

  

这可能是意图,但我没有看到任何方式阅读   标准的措辞就是这样。来自评论#0中的示例:

std::isnan(A());
     

没有算术类型的参数,所以没有任何子弹   26.8 / 11适用。过载集包含&#39; isnan(float)&#39; isnan(double)&#39;和&#39; isnan(long double)&#39;和&#39; isnan(浮点)&#39;应该   被选中。

因此,段落 11 DR 2086重新措辞并不会使调用 float double 的形式不正确和 long double 重载,否则可以使用非算术参数。

技术上有效但使用时可疑

尽管C ++ 11和C ++ 14标准没有将cmath函数限制为算术参数DR 2068,但认为26.811的意图是限制cmath函数只采用算术参数,显然是为了填补C ++ 14中的漏洞,但没有提供足够强大的限制。

依赖于在未来版本的标准中可能变得格式不正确的功能似乎值得怀疑。由于我们有实现差异,任何依赖于将非算术参数传递给cmath函数的代码都是不可移植的,因此仅在有限的情况下才有用。我们有一个替代解决方案,即明确地将非算术类型转换为算术类型,绕过整个问题,我们不再需要担心代码变得格式错误并且它是可移植的:

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

Potatoswatter指出,使用一元+也是一种选择:

std::isgreater( +s ,1.0)

更新

作为T.C.在C ++ 11中指出可以认为26.8113适用,因为参数既不是 long double double < / em>也不是整数,因此应该首先将类型S的参数强制转换为 float 。请注意,正如缺陷报告gcc所示,从未实现过这一点,到目前为止我也不知道clang