Spa*_*ter 5 c++ g++ cmath sqrt visual-c++
我有以下名为test.cpp的C++测试程序:
#include <cmath>
#include <iostream>
double sqrt(double d) { return std::sqrt(d); }
int main()
{
std::cout << "sqrt(4): " << sqrt(4) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
这是一些非常人为的代码,你可能已经猜到我只是想用Stroustrup做一个练习.他宣称双sqrt(双),并希望读者定义它.
我使用g ++ 4.8(来自Qt 5.1的MINGW版本)编译了上面的代码:
C:\Windows\Temp>g++ -o test.exe -g test.cpp
Run Code Online (Sandbox Code Playgroud)
当我运行生成的可执行文件时,Windows 7说"test.exe已停止工作".
要查看出了什么问题,我在GNU调试器中运行了test.exe.调试器命令和输出:
C:\Windows\Temp>gdb -q test.exe
Reading symbols from C:\Windows\Temp\test.exe...done.
(gdb) b main
Breakpoint 1 at 0x401625: file test.cpp, line 8.
(gdb) run
Starting program: C:\Windows\Temp\test.exe
[New Thread 12080.0x2ba0]
Breakpoint 1, main () at test.cpp:8
8 std::cout << "sqrt(4): " << sqrt(4) << std::endl;
(gdb) s
sqrt (d=4) at test.cpp:4
4 double sqrt(double d) { return std::sqrt(d); }
(gdb) s
sqrt (d=4) at test.cpp:4
4 double sqrt(double d) { return std::sqrt(d); }
(gdb) s
sqrt (d=4) at test.cpp:4
4 double sqrt(double d) { return std::sqrt(d); }
(gdb) s
sqrt (d=4) at test.cpp:4
4 double sqrt(double d) { return std::sqrt(d); }
(gdb) q
A debugging session is active.
Inferior 1 [process 12080] will be killed.
Quit anyway? (y or n) y
C:\Windows\Temp>
Run Code Online (Sandbox Code Playgroud)
从行为和警告,我推断std :: sqrt必须从全局命名空间调用sqrt - 这会导致我的函数被重复调用.
通过更改我的sqrt函数的名称或将其放在命名空间内来解决不需要的递归是很容易的.但是我想理解为什么std :: sqrt以调用:: sqrt的方式实现.我认为std命名空间的重点是防止在用户代码中使用不合格的名称进行名称冲突.
我看了一下<cmath>的GNU实现的源代码.但是,在跟随链中的几个#includes之后,我失去了踪迹.也许你可以更好地理解它:
00052 #include <math.h>
00053
00054 // Get rid of those macros defined in <math.h> in lieu of real functions.
....
00076 #undef sqrt
....
00081 namespace std
00082 {
....
00393 using ::sqrt;
00394
00395 inline float
00396 sqrt(float __x)
00397 { return __builtin_sqrtf(__x); }
00398
00399 inline long double
00400 sqrt(long double __x)
00401 { return __builtin_sqrtl(__x); }
00402
00403 template<typename _Tp>
00404 inline typename __enable_if<double, __is_integer<_Tp>::_M_type>::_M_type
00405 sqrt(_Tp __x)
00406 { return __builtin_sqrt(__x); }
....
00437 }
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这不仅仅是一个GNU难题.使用Visual C++编译器而不是g ++进行编译会产生以下警告:
C:\Windows\Temp>cl /nologo /EHsc test.cpp
test.cpp
c:\windows\temp\test.cpp(4) : warning C4717: 'sqrt' : recursive on all control
paths, function will cause runtime stack overflow
Run Code Online (Sandbox Code Playgroud)
我想在StackOverflow上提出这个问题是个公平的问题.:)
运行生成的可执行文件会导致预期的结果:"test.exe已停止工作".
问题是从C标准库继承的函数,例如<cmath>
函数,有点像有趣的野兽:它们看起来好像生活在命名空间中,std
但实际上它们是extern "C"
生活在全局命名空间中的函数.基本上std::sqrt(x)
有效地调用::sqrt(x)
恰好是您刚定义的函数的调用!
我没有检查C++标准在全局命名空间中对这些名称的描述,但我相当肯定它将它们归类为保留名称.也就是说,你最好不要::sqrt
以任何形状或形式定义.在合适的命名空间中定义函数,你会没事的.
好的,我查了一下.相关条款是17.3.24 [defns.reserved.function]:
保留功能
一个函数,指定为C++标准库的一部分,必须由实现定义[注意:如果C++程序为任何保留函数提供定义,则结果是未定义的. - 尾注]
......和17.6.4.3.3 [extern.names]第3和第4段:
使用外部链接声明的标准C库中的每个名称都保留给实现,以便
extern "C"
在名称空间std
和全局名称空间中用作具有链接的名称.使用外部链接声明的标准C库中的每个函数签名都保留给实现,以用作具有两个
extern "C"
和extern "C++"
链接的函数签名,或者作为全局命名空间中的命名空间范围的名称.
17.6.4.3.3/2在头中用外部链接声明的每个全局函数签名保留给实现,以指定具有外部链接的函数签名.
17.6.4.3.3/3使用外部链接声明的标准C库中的每个名称都保留给实现,以便extern "C"
在名称空间std
和全局名称空间中用作具有链接的名称.
17.6.4.3.3/4使用外部链接声明的标准C库中的每个函数签名都保留给实现,以用作具有两个extern "C"
和extern "C++"
链接的函数签名,或者作为全局命名空间中的命名空间范围的名称.
归档时间: |
|
查看次数: |
416 次 |
最近记录: |