言語仕様では<cmath>
、グローバル名前空間で標準関数を宣言(および定義)し、std
using-declarationsを使用してそれらを名前空間に組み込むことで、実装を実装できます。このアプローチが使用されるかどうかは指定されていません
20.5.1.2ヘッダー
4 [...]ただし、C ++標準ライブラリでは、宣言(Cでマクロとして定義されている名前を除く)は名前空間の名前空間スコープ(6.3.6)内にありますstd
。これらの名前(21から33節および付録Dで追加されたオーバーロードを含む)が最初にグローバル名前空間スコープ内で宣言され、次にstd
明示的なusing宣言(10.3.3)によって名前空間に注入されるかどうかは指定されていません。
どうやら、このアプローチ(GCCなど)に従うことを決定した実装の1つを扱っています。つまり、実装はを提供しますが::abs
、std::abs
単に「を参照」し::abs
ます。
この場合に残る1つの質問は、標準に加え::abs
て独自のを宣言できた::abs
理由、つまり、多重定義エラーがない理由です。これは、一部の実装(GCCなど)によって提供される別の機能が原因である可能性があります。これらは、標準関数をいわゆるウィークシンボルとして宣言しているため、独自の定義でそれらを「置き換える」ことができます。
弱いシンボルの交換:これら2つの要因が一緒にあなたが観察効果作成::abs
の交換でも結果をstd::abs
。これが言語標準とどれだけよく一致するかは別の話です...いずれにせよ、この動作に依存しないでください-言語によって保証されていません。
GCCでは、この動作は次の最小限の例で再現できます。1つのソースファイル
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
別のソースファイル
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
この場合、2番目のソースファイル内の::foo
("Goodbye!"
)の新しい定義もの動作に影響することを確認しN::foo
ます。両方の呼び出しが出力されます"Goodbye!"
。また::foo
、2番目のソースファイルからの定義を削除すると、両方の呼び出しがと「::foo
出力」の「元の」定義にディスパッチされます"Hello!"
。
上記の20.5.1.2/4によって与えられた許可は、の実装を単純化するためにあり<cmath>
ます。実装では、単純にCスタイルを含めてから<math.h>
関数を再宣言し、std
C ++固有の追加や調整を追加できます。上記の説明が問題の内部メカニズムを適切に説明している場合、その大部分は、関数のCスタイルバージョンの弱いシンボルの置き換え可能性に依存します。
我々は単に世界的に交換する場合に留意int
してdouble
上記のプログラムでは「予想通り」、(GCC下)のコードは動作します-それは、出力は以下となります-5 5
。これは、C標準ライブラリにabs(double)
機能がないために発生します。独自のを宣言するabs(double)
ことで、何も置き換えません。
しかしから切り替えた後場合int
にdouble
、我々はまた、から切り替えるabs
にfabs
は、元の奇妙な振る舞いは、その咲き誇る(出力に再表示されます-5 -5
)。
これは上記の説明と一致しています。
abs
が正しくありません。