C++ コンパイラが関数を見つけることができません (名前空間関連)
-
20-08-2019 - |
質問
私は Visual Studio 2008 で C++ プログラミングの課題に取り組んでいます。次の名前空間階層を定義するファイルが提供されました (名前はこの投稿のためだけにあり、「名前空間 XYZ-NAMESPACE」が冗長であることはわかっています)。
(MAIN-NAMESPACE){
a bunch of functions/classes I need to implement...
(EXCEPTIONS-NAMESPACE){
a bunch of exceptions
}
(POINTER-COLLECTIONS-NAMESPACE){
Set and LinkedList classes, plus iterators
}
}
MAIN-NAMESPACE の内容は一連のファイルに分割されており、何らかの理由で Set と LinkedList の両方の演算子<< が完全に MAIN-NAMESPACE の外側にあります (ただし、Set と LinkedList のヘッダー ファイル内にあります)。セットバージョンは次のとおりです。
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set<T>& set)
ここで問題があります:次のデータ構造があります。
Set A
Set B
Set C
double num
これは、MAIN-NAMESPACE 内のクラス内に存在するように定義されています。クラスのインスタンスを作成し、セットの 1 つを出力しようとすると、次のように表示されます。エラー C2679:バイナリ '<<' :'const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set' 型の右側のオペランドを取る演算子が見つかりません (または受け入れ可能な変換がありません)
ただし、 main() 関数を記述して Set A を作成し、それに値を入力して演算子を使用すると、機能します。
何が問題なのかわかりますか?(注記:思いつく限りの using と include の組み合わせを試してみました)。
解決 2
OK、私はこれを考え出しました。 そこに名前空間内の別のオペレータを<<既存程度jpalecekの直感は(どうやら私はそれをコメントアウトするのを忘れ)正しかったです。
名前空間の検索規則は、最初の関数呼び出しの名前空間で検索を開始し、外側の名前空間を検索し、右のグローバル名前空間(一致が見つからない場合、それは引数依存ルックアップを行い)まで。道に沿って、それはオペレータのためのいくつかの一致が見つかった場合しかし、<<、それは関係なく、ケースがここにあったように、これらの機能に使用されるタイプは、互換性がないかもしれないという事実のため、検索を停止します。
ソリューションは、(私はすることはできませんよ)MAIN-名前空間にそれを含める、または「使用して::演算子を<<」をグローバル名前空間からインポートするかのいずれかです。
他のヒント
ストレンジ - 異なる名前空間に悪い習慣を型に関連付けられた機能を自由にされて入れていても、グローバルな名前空間宣言は常に表示されます。
。MAIN-NAMESPACE
はoperator<<
で、全く関係のないタイプのために、おそらく、そこにされていません - 私は考えることができる唯一のことは、グローバル名前空間内の1つの影でしょうMAIN-NAMESPACE
に同じ名前のその宣言はありますか?もしそうなら、あなたはusing ::operator<<
でMAIN-NAMESPACE
宣言によってそれを修正する必要があります。例:
namespace A
{
namespace B
{
class C{};
}
}
void f(A::B::C*);
namespace A
{
void f(int*); // try commenting
using ::f; // these two lines
void g()
{
B::C* c;
f(c);
}
}
明示的に関数を呼び出してみ?
::operator<<( cout, myObj );
、明示的に呼び出してみます。
あなたは現在の名前空間の中に隠されているグローバル関数を呼び出したい場合は、あなたの詳細については、を持つ関数の前に::ローカル機能をバイパスし、グローバル関数を呼び出すために。
明示的に関数を呼び出してみ?
::演算子<<(COUT、このmyobj);
はい、それは動作しません!
これはでf関数を見つけようとします の場所で現在の名前空間( 呼び出す)または囲む名前空間で C1とC2タイプの(namespace1、 namespace2 :: namespace3)、それがされます 内の他の名前空間をしようとしません 検索ます。
だから私はこの権利を得た場合を見てみましょう:私はグローバルな名前空間(オペレータは<<あったように)であったため、main()関数が働いていたから<<演算子を呼び出す理由があります。 クラスはありませんグローバル名前空間にあったし、グローバル名前空間へのコンパイラを指摘し、それには変数がありませんでしたので、私は実装クラスから呼び出すときに、それが失敗した理由があります。
OKの人が具体的な例を求めたので、ここでのコードの関連部分です。 // Disclamerは:スリムケースで私のユニの誰かがこれを見て、提出ファイルに遭遇した、と私はそれをコピーしたり、何かを決定し、私の学生数は311670137
でありますこれは、ヘッダファイルSet.hある
namespace MTM {//This is the MAIN-NAMESPACE
namespace PointerCollections {
(ITERATORS AND PREDICATE CLASSES)
template<typename T>
class Set {
public:
/////////////////////////////////
// Definitions
/////////////////////////////////
private:
/////////////////////////////////
// Definitions
/////////////////////////////////
};
///////////////////////////////////////////////////////////////////////////////
// The implementation part.
///////////////////////////////////////////////////////////////////////////////
}
}
// operator<< - the same a Set::print(std::ostream& os,
// const BinaryPredicate<T>& predicate)
// function called with the 'predicate' parameter omitted
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MTM::PointerCollections::Set<T>& set){
set.print(os);
return os;
}
これは、私は別のファイルに定義されたものです
namespace MTM {
using std::ostream;
class Schedule {
public:
///////////////////
//Definitions, including:
///////////////////
void registerStation(string stationName);
void reportRegisteredStations(std::ostream& outputStream) const;
private: //My database
//All the classes Set recieves are defined elsewhere
Set<RegisteredStation> places;
Set<BusLine> busses;
Set<TrainLine> trains;
double tarifForBuses;
double tarifForTrains;
};
}
そして、ここでの主からです。
Schedule s();
s.registerStation("1");
s.reportRegisteredStations(cout);//This invokes the error. Definition follows:
reportRegisteredStationsは以下のように定義されます
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{
outputStream<<places;
}
これは私のために動作します。
#include <iostream>
#include <string>
using std::string;
namespace MTM {//This is the MAIN-NAMESPACE
namespace PointerCollections {
template<typename T>
class Set {
};
}
}
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MTM::PointerCollections::Set<T>& set){
return os;
}
namespace MTM {
using std::ostream;
using PointerCollections::Set;
class Schedule {
public:
///////////////////
//Definitions, including:
///////////////////
void registerStation(string stationName);
void reportRegisteredStations(std::ostream& outputStream) const;
private: //My database
//All the classes Set recieves are defined elsewhere
Set<int> places;
Set<int> busses;
Set<int> trains;
double tarifForBuses;
double tarifForTrains;
};
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{
outputStream<<places;
}
}
int main()
{
MTM::Schedule s;
s.reportRegisteredStations(std::cout);
}
修正:以下のテキストは、g++ ファミリのコンパイラの経験に基づいています。回答へのコメントの後、標準を読み直しました(通常の名前検索ではADLが使用され、通常の名前検索では演算子<<を見つける必要があると記載されています)。私も試してみました コモー コンパイラ (私が知る限り最も標準に準拠したコンパイラ) を実行すると、シンボルが見つかります。これは g++ の問題のようです (バージョン 3.3、4.1、4.3 を試しました)。
元の答え:
Koening ルックアップを検索します (技術的には ADL:引数に依存した検索)。
簡単に言うと、次のクラスがあるとします。
namespace test {
class A {};
}
ストリーム挿入演算子は次のように定義する必要があります。
namespace test {
std::ostream& operator<<( std::ostream&, A const & );
}
関数または演算子は、それが受け取る引数の 1 つと同じ名前空間で定義する必要があります。(*)
コンパイラが次のような関数呼び出しを見つけた場合:
namespace test2 {
void g() {
namespace1::class1 c1;
namespace2::namespace3::class2 c2;
f( c1, c2 );
}
}
を見つけようとします f 現在の名前空間 (呼び出し場所) または c1 および c2 タイプの周囲の名前空間 (namespace1、namespace2::namespace3) で関数を実行しますが、検索では他の名前空間は試行されません。
(*) この場合、できることはかなり制限されます。 テスト std 名前空間に関数を追加することはできないため (テンプレートの特殊化のみ)。
元の投稿の終わり.
たとえ前にコメントしたように、これがコンパイラの問題である可能性がある場合でも、ユーザー定義型を操作するすべてのフリー関数をその型自体と同じ名前空間で定義するのが一般的な使用法であり、推奨されます。