9

すべてのクラスをバージョン管理された名前空間に配置するC++サードパーティライブラリを使用しています。これを呼び出しましょうtplib_v44。また、一般的な名前空間エイリアスも定義します。

namespace tplib = tplib_v44;

ジェネリック名前空間を使用して、自分の.hファイルでライブラリのメンバーを前方宣言する場合...

namespace tplib { class SomeClassInTpLib; }

...サードパーティライブラリ(後で.cpp実装ファイルに含まれています)のヘッダーでコンパイラエラーが発生します。

error C2386: 'tplib' : a symbol with this name already exists in the current scope

バージョン固有の名前空間を使用すると、すべてが正常に機能しますが、...ポイントは何ですか?これに対処するための最良の方法は何ですか?

[編集]将来の視聴者のための参考までに、これはICUライブラリでした。解決策(少なくとも私の状況では)は、受け入れられた回答へのコメントにあります。

4

5 に答える 5

4

これには醜い回避策があるようですが、良い解決策はありません。

ACE(まともな説明付き)Xerces(「これはc ++のしくみです」という卑劣なコメント付き)の場合、これらはこれを「一般的に」行うために使用できるマクロを定義します。

ACE_BEGIN_VERSIONED_NAMESPACE_DECL
class ACE_Reactor;
ACE_END_VERSIONED_NAMESPACE_DECL

XERCES_CPP_NAMESPACE_BEGIN
class DOMDocument;
class DOMElement;
XERCES_CPP_NAMESPACE_END

不幸なc++アーティファクトのようtplibです。これらのマクロを探してみてください。

この標準では、名前空間と名前空間エイリアスを異なるものとして扱います。名前空間として宣言しtplibているので、コンパイラが後でエイリアスを割り当てようとすると、両方にすることはできないため、コンパイラは文句を言います。

于 2010-06-12T00:34:15.657 に答える
2

あなたの問題は、tplibが実際の名前空間ではなくエイリアスであることが原因だと思います

バージョン管理はサードパーティのライブラリ内にあるため、使用できない場合がありますが、バージョン管理されていない名前空間内で(エイリアスではなく)バージョン管理された名前空間を使用すると、g++4.0.1および4.1.2で機能するようです。しかし、これはうまくいかないはずだと感じています...そして、私が気付いていない他の問題があるかもしれません。

//This is the versioned namespace
namespace tplib_v44
{
   int foo(){ return 1; }
}

//An unversioned namespace using the versioned one
namespace tplib
{
  using namespace tplib_v44;
}


//Since unversioned is a real namespace, not an alias you can add to it normallly.
namespace tplib
{
   class Something {};
}


int main()
{
  //Just to make sure it all works as expected
  tplib::foo();
}
于 2010-06-12T01:05:39.190 に答える
0

えーと…あなたの言っていることが後ろ向きに見えます。tplibまったく逆に、クラスを名前空間のメンバーとして宣言しようとする意味は何ですか?(名前空間ではなく、名前空間のエイリアスであることを一瞬忘れてしまうため、エラーが発生します。)

名前空間と名前空間エイリアスに基づいて構築された、ある種のバージョン管理システムがあることは明らかです。クラスが名前空間の特定の「バージョン」(44など)で最初に導入された場合、それは宣言する必要のある名前空間です。なぜクラス宣言を「過去のバージョン」に、つまり過去のすべてのバージョンに押し込もうとしているのですか。名前空間の(43や30など)?あなたのクラスは以前のバージョンには存在しなかったので、そこに強制することは想定されていません。

于 2010-06-12T00:49:47.607 に答える
0

編集:私は質問の要点を見逃していると指摘されました-無視してください!

他の回答で強調されている問題に加えて、そもそもサードパーティが定義した名前空間に独自のコードを追加しようとしているのではないかと少し心配しています。

名前空間は、競合するシンボル(クラス、typdef、列挙型など)を独自の名前空間に配置することで衝突を防ぐために存在します。これにより、潜在的に同一の部分的に修飾されたシンボルから一意の完全修飾シンボルを開発します。サードパーティの名前空間に独自のコードを追加すると、(たとえば)後のバージョンで、サードパーティが同じシンボルを使用したいと判断した場合(独自のコードを追加するなど)、問題が発生する可能性がありますSomeClassInTpLib-突然、名前空間の名前の競合を防ぐことができます彼らの醜い頭を育てます。stdこれが、名前空間に追加することは一般的に悪い習慣である理由です。

この問題を完全に回避するはるかに安全な解決策は、単に独自の名前空間を使用することです。それまたは類似のものと呼んでtplib_exも、関連付けは明確になりますが、競合は問題にならず、エイリアス関連の問題も解消されます。

于 2010-06-12T01:17:08.833 に答える
0

ポイントは何ですか?

名前空間に追加のものを追加できないようにするのは、おそらく彼らがすぐにもっと多くの名前を追加しようと考えているためです(そして、バージョン管理された名前空間を使用しているという事実がそれを示唆しているかもしれません)。ただし、これらは単なる推測です。これには、より合理的であると思われる名前空間での前方宣言を防ぐという副作用があるため、彼らの側ではプログラミングの習慣が悪いと思います。

これに対処するための最良の方法は何ですか?

最善の方法はありませんが、マクロの使用は避けてください。マクロは見苦しく、見た目も良くありません(大文字のものはすべて好きではありません)。「バージョンを変更するとどうなりますか?」という懸念がある場合。(理論的には、すべてのコードで「v44」を「v45」に変更する必要があります)

次に、1つのヘッダーを使用して、必要なものすべてを前方宣言します。

TpLibForwards.hpp

#ifdef XXXXXX_TPLIB
   #error "XXXXXX_TPLIB is already taken, change to something else"
#endif
#define XXXXXX_TPLIB  tplib_v44 
//... and that's why I don't like keeping macros around..

namespace XXXXXX_TPLIB
{

    // FORWARD DECLARATIONS
    class A1;
    class A2;
    //...
}
namespace tplib = XXXXXX_TPLIB;
#undef XXXXXX_TPLIB

彼らがライブラリを変更した場合、あなたはただ1つの変更を1つのファイルに適用する必要があります。多くのプログラマーは、前方宣言を1つのポイントですでに保持しています。これは、前方宣言がはるかに管理しやすく、多くのものを前方宣言する必要がある場合に備えて、他のヘッダーをよりクリーンで読みやすくするためです。

#include <TpLibForwards.hpp> // my forwards declarations
于 2016-11-17T08:51:11.533 に答える