[解決済み] ある宣言がstd名前空間に影響を与えることは可能か?
質問
#include <iostream>
#include <cmath>
/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
return a > 0? -a : a;
}
int main() {
int a = abs(-5);
int b = std::abs(-5);
std::cout<< a << std::endl << b << std::endl;
return 0;
}
という出力になると思っていました。
-5
であり
5
が、出力されるのは
-5
と
-5
.
なぜこのようなケースが起こるのでしょうね。
の使用と関係があるのでしょうか?
std
の使用と関係があるのでしょうか?
どのように解決するのですか?
言語仕様
では
を実装することができます。
<cmath>
の標準的な関数を宣言(定義)することにより
グローバル
名前空間で宣言し、それを名前空間
std
をusing-declarationsによって呼び出す。この方法が使われるかどうかは未定です
20.5.1.2 ヘッダ
4 [...] しかし、C++標準ライブラリでは、宣言は(Cでマクロとして定義されている名前を除いて)名前空間のスコープ内(6.3.6)にあるstd
. これらの名前(第21項から第33項までに追加されたオーバーロードを含む)が、C言語の名前空間の範囲(6.3.6)内にあるかどうかは特定されていません。 がグローバルな名前空間スコープで最初に宣言され,その後,名前空間std
に注入されるかどうかは特定されていません。
どうやら、このアプローチに従うことを決めた実装の一つを扱っているようです(例えば、GCCなど)。つまり、あなたの実装では
::abs
を提供し、一方
std::abs
は単に
::abs
.
この場合、一つ疑問が残るのは、なぜ標準の
::abs
を宣言することができたのかということです。
::abs
を宣言できたはずです。つまり、多重定義エラーは発生しません。これは、いくつかの実装(例えばGCC)が提供する別の機能によって引き起こされるかもしれません: 彼らは、標準的な関数をいわゆる
弱いシンボル
このため、独自の定義でそれらを "replace"することができます。
これら 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();
}
この場合、新しい定義の
::foo
(
"Goodbye!"
) の挙動にも影響を与えます。
N::foo
. どちらの呼び出しも
"Goodbye!"
. そして、もし
::foo
の定義を削除すると、両方の呼び出しが "original" の定義にディスパッチされます。
::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
).
これは上記の説明と一致します。
関連
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] 文字列の単語を反復処理するにはどうすればよいですか?
-
[解決済み] using namespace std;」はなぜバッドプラクティスだと言われるのですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] なぜテンプレートはヘッダーファイルでしか実装できないのですか?
-
[解決済み] Linux上で動作するC++コードのプロファイリングを行うにはどうすればよいですか?
-
[解決済み】C/C++の"-->"演算子とは何ですか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++でユーザー入力を待つ【重複あり
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】C++の余分な資格エラー
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み】クラスのコンストラクタへの未定義参照、.cppファイルの修正も含む
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み】c++で.txtファイルから2次元の配列に読み込む