13

重複の可能性:
名前空間ブロックで#includeをラップすることをお勧めしますか?

logグローバル名前空間( )にクラスを持つプロジェクトがあります::log

したがって、当然のことながら、その後#include <cmath>、ログクラスのオブジェクトをインスタンス化しようとするたびに、コンパイラはエラーメッセージを表示します。これ<cmath>は、グローバル名前空間を多数の3文字のメソッド(そのうちの1つは対数関数)で汚染するためlog()です。

したがって、3つの可能な解決策があり、それぞれに固有の醜い副作用があります。

  • ログクラスをそれ自体の名前空間に移動し、常に完全修飾名でアクセスします。ロガーはできるだけ使いやすいはずなので、これは避けたいと思います。
  • mathwrapper.cppを含むプロジェクト内の唯一のファイルであるファイルを作成<cmath>し、必要なすべての<cmath>関数を。のラッパーを介して使用できるようにしnamespace mathます。必要なすべての数学関数のラッパーを作成する必要があるため、このアプローチを使用したくありません。これにより、呼び出しペナルティが追加されます(-fltoコンパイラフラグによって部分的にキャンセルされます) 。
  • 私が現在検討している解決策:

交換

#include <cmath>

namespace math {
#include "math.h"
}

次に、を介して対数関数を計算しmath::log()ます。

私はそれを試してみましたが、実際、期待どおりにコンパイル、リンク、実行されます。ただし、複数の欠点があります。

  • コードは完全修飾名で関数にアクセスし、C ++での使用は非推奨で<cmath>あるため、(明らかに)使用することは不可能です。<cmath>
  • 猛禽類に襲われて生きたまま食べられるような、本当に本当に悪い気持ちになります。

だから私の質問は:

  • 名前空間にincludeディレクティブを入れることを禁止する推奨事項/規則などはありますか?
  • 何かがうまくいかない可能性があります

    • 異なるC標準ライブラリの実装(私はglibcを使用しています)、
    • さまざまなコンパイラ(私はg ++ 4.7、-std = c ++ 11を使用)、
    • リンク?
  • これをやってみたことがありますか?
  • グローバル名前空間から数学関数を削除する別の方法はありますか?

私はstackoverflowでいくつかの同様の質問を見つけましたが、ほとんどは他のC ++ヘッダーを含めることに関するものでしたが、これは明らかに悪い考えであり、Cライブラリのリンク動作について矛盾したステートメントが作成されていませんでした。#include <math.h>また、中に追加で入れるといいのextern "C" {}でしょうか?

編集

そこで、おそらく他のすべての人が行っていることを実行し、すべてのコードをプロジェクトの名前空間に配置し、を含めるときに完全修飾名でロガーにアクセスすることにしました<cmath>

4

4 に答える 4

18

いいえ、検討している解決策は許可されていません。実際には、ヘッダーファイルの意味を変更していることを意味します。異なる名前の関数を宣言するように、すべての宣言を変更しています。

extern "C"これらの変更された宣言は、標準ライブラリ関数の実際の名前と一致しないため、リンク時に、許可されている宣言が行われていない限り、変更された宣言によって宣言された関数への呼び出しを解決する標準ライブラリ関数はありません。推奨されません-C標準ライブラリからの名前の場合。

ISO / IEC 14882:2011 17.6.2.2/3 [using.headers]は、C ++標準ライブラリの一部であるため、C標準ライブラリヘッダーに適用されます。

翻訳ユニットは、外部宣言または定義[*]の外側にのみヘッダーを含めるものとし、そのヘッダーで宣言されたエンティティへのその翻訳ユニットの最初の参照の前に字句的にヘッダーを含めるものとします。

[*]これには名前空間の定義が含まれます。

于 2012-09-07T19:37:35.750 に答える
6

ログクラスを独自の名前空間にtypedef namespace::log logger;配置し、より便利な方法で名前の衝突を回避するために使用してみませんか?

于 2012-09-07T19:51:45.270 に答える
2

クラスの名前を変更します。それほど大したことではありません。;-)

ただし、真剣に、標準ヘッダーの名前と衝突する名前をグローバル名前空間に配置することはお勧めできません。<cmath>C ++ 03は、の定義を明示的に許可していませんでした::log<cmath>しかし、既存の<math.h>(そしておそらく数学を含むいくつかのヘッダー用の既存の静的リンクライブラリ)の上に定義することの実用性のために、実装は慢性的にそれについて不適合でした。したがって、C ++ 11は既存の慣行を承認<cmath>し、すべてをグローバル名前空間にダンプできるようにします。C ++ 11は、ヘッダーを含めない場合でも、extern "C"リンケージで使用するためにこれらすべての名前を予約し、C++リンケージで使用するためにすべての関数シグネチャを予約します。しかし、それについては後で詳しく説明します。

C ++では、任意の標準ヘッダーで他の標準ヘッダーからの名前を定義できます(つまり、相互に含めることができます)。つまり、すべての標準ヘッダーで.を定義できます::log。したがって、使用しないでください。

さまざまな実装に関する質問への答えは、スキームが最初から機能している場合でも(保証されていません)、他の実装では、使用する(または将来同じTUで使用したい)ヘッダーがある可能性があるということです。あなたのログクラス)、それは、そしてあなたが治療を<cmath>与えなかったことを含みます。namespace math私の頭のてっぺんから、<random>私には候補者のようです。これは、数学関数とインラインで実装できる可能性のある一連の連続乱数分布を提供します。

提案しますLogが、大文字のクラス名が好きです。標準のタイプや機能とは常に異なるためです。

もう1つの可能性は、クラスを以前と同じように定義しstruct log、の代わりに使用することですlog。これは関数と衝突しません。理由は、CおよびC ++標準に時間をかけすぎた場合にのみ明らかになるためです(関数としてではなく、「C」の名前としてではなく、クラスlog名としてのみ使用します)。リンケージなので、予約された名前を侵害することはありません。それとは反対の外観にもかかわらず、C ++のクラス名は、Cのタグのように、他の名前からの並列ユニバースに存在します。struct

残念ながらstruct log、単純な型識別子ではないため、たとえば、を使用して一時的なものを作成することはできませんstruct log(VERY_VERBOSE, TO_FILE)。単純型識別子を定義するには:

typedef struct log Log;
Log(VERY_VERBOSE, TO_FILE); // unused temporary object

記載されている使用例に基づいて、以下のコメントで私が言っていることの例。これは有効だと思いますが、よくわかりません。

#include <iostream>
#include <cmath>
using std::log; // to enforce roughly what the compiler does anyway

enum Foo {
    foo, bar
};

std::ostream &log(Foo f) { return std::cout; }

int main() {
    log(foo) << log(10) << "\n";
}
于 2012-09-07T21:08:16.727 に答える
2

これも醜いハックですが、リンカーの問題は発生しないと思います。からログ名を再定義するだけです<math.h>

 #define log math_log
 #include <math.h>
 #undef log

このログを使用した数学のインライン関数で問題が発生する可能性がありますが、幸運なことに...

数学log()は引き続きアクセス可能ですが、簡単ではありません。使用したい関数内で、実際の宣言を繰り返すだけです。

    int somefunc() {
        double log(double); // not sure if correct
        return log(1.1);
    }
于 2012-09-07T21:08:58.067 に答える