<cmath>
面白いヘッダーです。同義語::sqrt
を
作成することは許可されています(必須ではありません) 。std::sqrt
それを含める場合は、両方が存在すると想定するのが最善です(または、単に含める<math.h>
場合は、::sqrt
取得する必要があるすべてです)。あなたの場合におそらく起こっていることは、1)実際には(を介して)std::sqrt
の同義語であり、2)リンカーが最初のものを取得しているため、無限の再帰が発生することです。名前を変更する以外の唯一の解決策は
、名前空間に自分を配置することです。using
::sqrt
::sqrt
sqrt
編集:
明確にするために:上記はC++11です。以前のバージョンのC++では、グローバル名前空間に何も導入できませんでした。<cmath>
ただし、すべての実装で実行されたため、プラクティスを祝福するために標準が変更されました。(これは、コンパイラーを標準に準拠させる1つの方法だと思います。)
編集:
コメントの質問に答えて、ライブラリがシンボルを「ピックアップ」する方法に関するいくつかの追加情報。正式には、C ++標準によれば、プログラム内に同じ関数(同じ名前、名前空間、および引数タイプ)の2つの定義がない場合があります。2つの定義が別々の翻訳単位にある場合、動作は定義されていません。これを念頭に置いて、いくつかの実用的な考慮事項があります。
最初のものは、ライブラリの定義(または少なくとも従来の定義)と見なすことができます。ライブラリは、モジュールのセットであり、標準では翻訳ユニットです。(通常、ただし常にではありませんが、モジュールはコンパイルされたオブジェクトファイルで構成されます。)ただし、ライブラリにリンクしても、ライブラリ内のすべてのモジュールが取り込まれるわけではありません。ライブラリのモジュールは、未解決の外部を解決する場合にのみプログラムに組み込まれます。したがって、::sqrt
リンカがライブラリを調べる前にがすでに定義(解決)されている場合、ライブラリに含まれるモジュールは
::sqrt
プログラムの一部にはなりません。
実際には、ライブラリという用語は近年乱用されており、その意味が変わったと言えるほどです。特に、Microsoftが「動的にロードされたライブラリ」と呼んでいるもの(および以前はUnixで「共有オブジェクト」と呼ばれていたもの)は、従来の意味でのライブラリではなく、上記はそれらに当てはまりません。ただし、動的ローダーがどのように機能するかによっては、他の問題も発生します。Unixの場合、複数の共有オブジェクトが同じシンボルを持っていると、すべてが最初にロードされたものに解決されます(デフォルトでは、これはに渡されるオプションによって制御できますdlopen
)。Windowsの場合、デフォルトでは、シンボルは可能であればDLL内で解決されます。あなたの場合、std::sqrt
がインライン関数であるか、として指定されているusing ::sqrt
場合、これはを呼び出すDLLになります
std::sqrt
; ヘッダーにある場合__declspec(dllexport)
、これはの実装を含むDLLになります
std::sqrt
。
最後に、今日のほとんどすべてのリンカーは、何らかの形の弱参照をサポートしています。これは通常、テンプレートのインスタンス化に使用されます。std::vector<int>::vector( size_t, int )
たとえば、それを使用するすべての翻訳単位でインスタンス化されますが、「弱い」記号として使用されます。次に、リンカは1つを選択し(おそらく最初に遭遇しますが、指定されていません)、他のすべてを破棄します。この手法は主にテンプレートのインスタンス化に使用されますが、コンパイラーは弱参照を使用して任意の関数を定義できます(関数がインラインの場合は定義します)。この場合、定義が異なる場合(
::sqrt
)、このプログラムは1つの定義規則に違反しているため、違法であると言えます。ただし、結果は未定義の動作であり、診断は必要ありません。たとえば、インライン関数または関数テンプレートを2つの異なる変換単位で異なる方法で定義した場合、エラーが発生することはほとんどありません。コンパイラが実際にそれらをインライン化しない場合、リンカは1つを選択し、両方の変換ユニットでそれを使用します。あなたの場合(::sqrt
)、これが当てはまるとは思えません。これは実際のライブラリ関数であり、インラインではないと思います。(インライン化されている場合、定義はヘッダー<cmath>
にあり、両方の定義が同じ翻訳単位にあるため、重複した定義エラーが発生します。)