70

以下のコードでは、簡単なlog関数を定義しています。main私はそれを呼ばないようにしています。と呼びますstd::log。それにもかかわらず、私自身logは呼ばれています。「ログ」が表示されます。画面上で。誰かが理由を知っていますか?私はG++4.7とclang++3.2を使用しています。

#include <iostream>
#include <cmath>

double log(const double x) { std::cout << "log!\n"; return x; }

int main(int argc, char *argv[])
{
  std::log(3.14);
  return 0;
}
4

5 に答える 5

58

C ++標準17.6.1.2段落4(私の強調):

第18条から第30条および付属書Dに記載されている場合を除き、各ヘッダーの内容は、C標準ライブラリ(1.2)またはC Unicode TRで指定されているようcnameに、対応するヘッダーの内容と同じである必要があります。name.h包含。ただし、C ++標準ライブラリでは、宣言(Cでマクロとして定義されている名前を除く)は、名前空間の名前空間スコープ(3.3.6)内にありますstdこれらの名前が最初にグローバル名前空間スコープ内で宣言され、次にstd明示的なusing-declarations(7.3.3)によって名前空間に挿入されるかどうかは指定されていません。

g ++は後者の方法で実行するため、同じヘッダーファイルの一部をCおよびC++で再利用できます。したがって、g++はdouble log(double)グローバル名前空間で宣言および定義できます。

セクション17.6.4.3.3パラグラフ3および4:

外部リンケージで宣言された標準Cライブラリの各名前は、名前空間とグローバル名前空間のextern "C"両方でリンケージ付きの名前として使用するために実装に予約されています。std

外部リンケージで宣言された標準Cライブラリの各関数シグニチャは、extern "C"extern "C++"リンケージの両方を持つ関数シグニチャとして、またはグローバル名前空間の名前空間スコープの名前として使用するために実装に予約されています。

そして、セクション17.6.4.3パラグラフ2の上部にあります。

プログラムが、この句で明示的に許可されている場合を除き、予約されているコンテキストで名前を宣言または定義する場合、その動作は未定義です。

一方、あなたはいかなる方法でも宣言または定義することはできません::log

ただし、g++ツールチェーンでエラーメッセージが表示されないのは残念です。

于 2012-08-09T23:02:46.230 に答える
9

何が起こるか、私は期待します、それはstd::log単にに委任すること::logです。残念ながら、オーバーロードを::log提供するだけfloatであり、あなたは親切にdoubleオーバーロードを提供し、あなたのより良いマッチを作ります。しかし、それがオーバーロードセットでどのように考慮されるのかはまだわかりません。

于 2012-08-09T22:46:28.593 に答える
9

libstdc ++では、次のcmathように表示されます。

using ::log;

つまり、math.h関数をグローバル名前空間からに取り込みstdます。残念ながら、の実装を提供しているdouble log(double)ため、リンカはmathlibの実装を使用しません。したがって、間違いなくlibstdc++のバグです

編集:バージョンstd::logを明示的に要求しているときにCライブラリとの干渉に悩まされるべきではないため、これはlibstdc++のバグであると私は主張しstd::ます。もちろん、標準ライブラリ関数をオーバーライドするこの方法は、C言語の古い「機能」です。

編集2:標準では、グローバル名前空間からに名前を持ち込むことは実際には禁止されていないことがわかりましたstd。したがって、結局のところバグではなく、実装の詳細の結果にすぎません。

于 2012-08-09T22:47:21.983 に答える
6

C ++では、コンパイラーはグローバル名前空間にCライブラリーを自由に実装し、それに委任します(これは実装定義です)。

17.6.1.2.4第18条から第30条および付属書Dに記載されている場合を除き、各ヘッダーcnameの内容は、C標準ライブラリ(1.2)またはCで指定されている対応するヘッダーname.hの内容と同じでなければなりません。 Unicode TR、必要に応じて、インクルードによるかのように。ただし、C ++標準ライブラリでは、宣言(Cでマクロとして定義されている名前を除く)は、名前空間stdの名前空間スコープ(3.3.6)内にあります。これらの名前が最初にグローバル名前空間スコープ内で宣言され、次に明示的なusing-declarations(7.3.3)によって名前空間stdに挿入されるかどうかは指定されていません。

一般的に、C標準ライブラリの1つと同じシグニチャを持つ関数を作成することは避けたいと思います。C ++標準では、コンパイラーが選択した場合、これらの署名を自由に使用できるようになります。つまり、同じ署名を使用しようとすると、コンパイラーと戦う可能性があります。したがって、奇妙な結果が得られます。

ただし、リンカのエラーまたは警告が予想されますが、これを報告する価値があると思います。

[編集]

うわー、忍者だ。

于 2012-08-09T23:06:29.897 に答える
0

グローバル名前空間でオーバーライドしたためです。たとえば、 Nimのようなより安全でクリーンな言語に移行したくない場合は、名前空間を使用することでその危険を回避できます。

名前空間デモの適切な使用

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see http://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace

// Silently overrides std::log
//double log(double d) { return 420; }

namespace uniquename {
    using namespace std;  // So we don't have to waste space on std:: when not needed.

    double log(double d) {
        return 42;
    }

    int main() {
        cout << "Our log: " << log(4.2) << endl;
        cout << "Standard log: " << std::log(4.2);
        return 0;
    }
}

// Global wrapper for our contained code.
int main() {
    return uniquename::main();
}

出力:

Our log: 42
Standard log: 1.43508
于 2015-05-06T01:51:41.337 に答える