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

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

给定以下用户定义的类型 S,它具有到 double 的转换函数:

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

以及以下对 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) ;
}

此代码使用 libstdc++ ( see it live ) 使用 gcc 编译,但使用 使用 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 &&
^

isless 的类似错误和 isfinite , 所以 libc++ 期望这些调用的参数是 arithmetic types S 不是,我们可以通过查看 libc++ cmath header 的源代码来确认这一点.虽然,libc++ 中的所有 cmath 函数对算术类型的要求并不一致。

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

最佳答案

长话短说

根据标准,将非算术类型作为参数传递给 cmath 是有效的功能但有缺陷报告2068争论的初衷是cmath函数应限制为算术类型,并且使用非算术参数似乎最终可能会变得格式错误。因此,尽管根据缺陷报告 2068,使用非算术类型作为参数在技术上是有效的似乎值得怀疑.

详情

cmath header 包含在标准草案部分26.8 [c.math]math.h 中定义的每个函数提供额外的 floatlong double 重载需要一个double 参数和进一步的段落11提供足够的重载并说:

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

这似乎在 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中的工作方式:

My impression is that this rule set is probably more generic as intended, my assumption is that it is written to mimic the C99/C1x rule set in 7.25 p2+3 in the "C++" way [...] (note that C constraints the valid set to types that C++ describes as arithmetic types, but see below for one important difference) [...]

并说:

My current suggestion to fix these problems would be to constrain the valid argument types of these functions to arithmetic types.

并改写了部分 26.811说(强调我的):

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any arithmetic argument corresponding to a double parameter has type long double, then all arithmetic arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any arithmetic argument corresponding to a double parameter has type double or an integer type, then all arithmetic arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arithmetic arguments corresponding to double parameters are effectively cast to are effectively cast to have type float.

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

好吧,尽管意图如此,但从技术上看,它在 libc++ bug report: incorrect implementation of isnan and similar functions 中的讨论中的评论中仍然有效。 :

That may have been the intent, but I don't see any way to read the standard's wording that way. From the example in comment#0:

std::isnan(A());

There are no arguments of arithmetic type, so none of the bullets in 26.8/11 apply. The overload set contains 'isnan(float)', 'isnan(double)', and 'isnan(long double)', and 'isnan(float)' should be selected.

因此,DR 2086 的改写段落 11 不会导致调用 floatdoublelong double 重载的格式错误带有非算术参数。

技术上有效但使用有问题

所以虽然C++11和C++14标准不限制cmath算术参数的函数 DR 2068争论 26.8 的意图款11是限制cmath函数只接受算术参数,显然是为了弥补 C++14 中的漏洞,但没有提供足够强的限制。

依赖一个在未来版本的标准中可能变得格式错误的特性似乎是有问题的。由于我们有实现分歧,任何依赖于将非算术参数传递给 cmath 的代码这些案例的功能是不可移植的,因此仅在有限的情况下有用。我们有一个替代解决方案,即显式地将非算术类型转换为算术类型,这绕过了整个问题,我们不再需要担心代码变得病态并且它是可移植的:

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

正如 Potatoswatter 指出的那样使用一元 +也是一个选项:

std::isgreater( +s ,1.0)

更新

作为 T.C.在 C++11 中指出,可以认为 26.811项目符号 3适用,因为参数既不是 long doubledouble 也不是整数,因此应该是 S 类型的参数应该首先转换为float。注意,如缺陷报告所示 gcc从来没有实现过这个,据我所知也没有clang .

关于c++ - 将非算术类型作为参数传递给 cmath 函数是否有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30766491/

相关文章:

c++ - 如何将一种类型的 unique_ptr 复制到另一种类型

c++ - 构建/销毁过程中的虚拟调用

c++ - Boost:如何创建一个线程以便可以控制它的所有标准输出、标准错误?

c++ - MPI_Win_free 导致无效窗口错误

c++ - 'instantiation' 对模板的意义

c++ - iter_swap 可以专门化吗?

c++ - 使用模板对单个 c++ 对象和 std::pair 对象进行抽象

c++ - 是否允许为标准库类型和内置类型重载 operator+?

c++ - 不可移动类的对象不可抛出?

c++ - 重载运算符<< : cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’