Sha*_*our 5 c++ cmath language-lawyer c++11 c++14
给定以下用户定义类型S
并将转换函数加倍:
struct S
{
operator double() { return 1.0;}
};
Run Code Online (Sandbox Code Playgroud)
以及使用以下类型调用cmath函数S
:
#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) ;
}
Run Code Online (Sandbox Code Playgroud)
此代码编译与gcc
使用libstdc++
(见直播),但clang
利用libc++
它生成了几个调用(误差在现场观看),出现以下错误的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 &&
^
Run Code Online (Sandbox Code Playgroud)
和类似的错误isless和ISFINITE,因此libc++
预计,这些调用的参数是算术类型,其S
不是,我们可以通过转到源证实这一点的libc ++ CMATH头.虽然,算术类型的要求在所有cmath
函数中并不一致libc++
.
所以问题是,将非算术类型作为参数传递给cmath
函数是否有效?
TL; DR
根据标准,将非算术类型作为参数传递给cmath
函数是有效的,但缺陷报告2068
认为最初的意图是cmath
函数应该限制为算术类型,并且看起来使用非算术参数最终会变得格式不正确.因此,尽管使用非算术类型作为参数在技术上有效,但鉴于缺陷报告,这似乎是有问题的2068
.
细节
所述CMATH头被覆盖在标准牵伸部26.8
[c.math]提供了额外的浮体和长双为在定义的各函数重载math.h中,需要一个双参数,并进一步,段落11
提供了足够的过载并说:
此外,还应有足够的额外过载来确保:
- 如果对应于double参数的任何参数的类型为long double,则对应于double参数的所有参数都有效地转换为long double.
- 否则,如果对应于double参数的任何参数具有double类型或整数类型,则对应于double参数的所有参数都将有效地转换为double.
- 否则,对应于double参数的所有参数都被有效地转换为float.
这似乎在C++ 11中有效
在C++ 11中,26.8
[c.math]部分不包含任何禁止cmath
函数非算术参数的限制.在每种情况下,我们都有一个带有双参数的过载,这些应该通过重载分辨率来选择.
缺陷报告2086
但是对于C++ 14,我们有缺陷报告2086:对数学函数的过度泛型类型支持,它认为section 26.8
[c.math]的原始意图是限制cmath
函数仅对算术类型有效,这将模仿它们的方式在C工作:
我的印象是这个规则集可能更符合预期,我的假设是它是用"C++"方式模仿7.25 p2 + 3中的C99/C1x规则集[...](注意C约束) C++描述为算术类型的类型的有效集合,但请参阅下面的一个重要区别)[...]
并说:
我目前解决这些问题的建议是将这些函数的有效参数类型约束为算术类型.
并改写部分26.8
段落11
(强调我的):
此外,还应有足够的额外过载来确保:
- 如果对应于double参数的任何算术参数具有long double类型,则对应于double参数的所有算术参数都被有效地转换为long double.
- 否则,如果对应于double参数的任何算术参数具有double类型或整数类型,则 对应于double参数的所有算术参数将被有效地转换为double.
- 否则,对应于double参数的所有算术参数都
被有效地强制转换为具有 float 类型.
所以这在C++ 14中无效?
好吧,尽管意图从技术上看仍然有效,因为在libc ++错误报告的讨论中的评论中提出:isnan和类似函数的错误实现:
这可能是意图,但我认为没有办法以这种方式阅读标准的措辞.从评论#0中的示例:
Run Code Online (Sandbox Code Playgroud)std::isnan(A());
没有算术类型的参数,因此26.8/11中没有任何项目符号适用.重载集包含'isnan(float)','isnan(double)'和'isnan(long double)',并且应该选择'isnan(float)'.
因此,DR 2086
第11段的重新措辞并不会使得调用浮点数,双重和长双重过载以及非算术参数可用.
技术上有效但可疑使用
因此,虽然C++ 11和C++ 14标准没有将cmath
函数限制为算术参数,但DR 2068
认为26.8
段落的意图11
是限制cmath
函数只采用算术参数,并且显然是为了填补C++ 14中的漏洞,但没有提供足够强大的限制.
依赖于在未来版本的标准中可能变得格式不正确的功能似乎是值得怀疑的.由于我们有实现差异,任何依赖于将非算术参数传递cmath
给这些情况的函数的代码都是不可移植的,因此仅在有限的情况下才有用.我们有一个替代解决方案,即明确地将非算术类型转换为算术类型,绕过整个问题,我们不再需要担心代码变得格式错误并且它是可移植的:
std::isgreater( static_cast<double>(s) ,1.0)
^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
正如Potatoswatter指出使用一元+
也是一种选择:
std::isgreater( +s ,1.0)
Run Code Online (Sandbox Code Playgroud)
更新
正如TC在C++ 11中指出的那样,可以认为26.8
段落11
子弹3
适用,因为参数既不是long double,double也不是整数,因此S
应该将类型的参数强制转换为float.请注意,正如缺陷报告所指出的那样,gcc
从未实现过这一点,而且迄今为止我也都知道clang
.
归档时间: |
|
查看次数: |
238 次 |
最近记录: |