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;
}
これはかなり不自然なコードであり、ご想像のとおり、Stroustrup を使って演習を行おうとしています。彼は double sqrt(double) を宣言し、読者にそれを定義してもらいたいと考えています。
g++ 4.8 (Qt 5.1 の MINGW リリースから) を使用して上記のコードをコンパイルしました。
C:\Windows\Temp>g++ -o test.exe -g test.cpp
結果の実行可能ファイルを実行すると、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>
動作と警告から、std::sqrt はグローバル名前空間から sqrt を呼び出す必要があると推測します。これにより、関数が繰り返し呼び出されます。
sqrt 関数の名前を変更するか、名前空間内に配置することで、不要な再帰を回避するのは簡単です。しかし、std::sqrt が ::sqrt が呼び出されるような方法で実装されている理由を理解したいと思います。std 名前空間の要点は、ユーザー コード内の修飾されていない名前との名前の衝突を防ぐことだと思いました。
<cmath> の GNU 実装のソース コードをのぞいてみました。ただし、チェーンのいくつかの #include をたどった後、トレイルを失いました。多分あなたはそれをもっと理解できるでしょう:
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 }
ちなみに、これは単なる GNU パズルではありません。g++ の代わりに Visual C++ コンパイラでコンパイルすると、次の警告が表示されます。
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
これは、StackOverflow で尋ねるのにふさわしい質問だと思います。:)
結果の実行可能ファイルを実行すると、期待どおりの結果が得られます: 「test.exe が動作を停止しました」。