C ++编译器无法找到函数(与命名空间相关)

时间:2009-01-29 23:49:08

标签: c++ namespaces operator-keyword

我在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的头文件中)。 这是Set版本:

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中的一个类。当我创建一个类的实例,并尝试打印其中一个集合时,它告诉我: 错误C2679:二进制'&lt;&lt;' :没有找到哪个运算符采用'const MAIN-NAMESPACE :: POINTER-COLLECTIONS-NAMESPACE :: Set'类型的右手操作数(或者没有可接受的转换)

但是,如果我只是编写一个main()函数,并创建Set A,请填充它,并使用运算符 - 它可以工作。

知道问题是什么? (注意:我尝试过任何组合使用和包括我能想到的)。

8 个答案:

答案 0 :(得分:2)

奇怪 - 尽管将与类型相关联的自由函数放到不同的命名空间是一种不好的做法,但全局命名空间声明始终可见。

我唯一能想到的是MAIN-NAMESPACE中具有相同名称的声明会影响全局命名空间中的声明 - 不存在operator<<,可能是完全不相关的类型, 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);
  }
}

答案 1 :(得分:2)

好的,我明白这一点。 jpalecek关于存在另一个运算符的直觉&lt;&lt;在命名空间中是正确的(显然我忘了评论它)。

命名空间的查找规则首先在函数调用的命名空间中开始搜索,然后搜索封闭的命名空间,直到全局命名空间(如果没有找到匹配,它将执行与参数相关的查找)。但是,如果它一直找到运算符&lt;&lt;的匹配,它将停止搜索,而不管这些函数中使用的类型可能不兼容,如此处的情况。

解决方案要么将其包含在MAIN-NAMESPACE中(我不允许),要么使用“using :: operator&lt;&lt;”从全局命名空间中导入它。

答案 2 :(得分:0)

尝试显式调用该函数?

::operator<<( cout, myObj );

答案 3 :(得分:0)

正如SoaBox指出的那样,请尝试明确地调用它。

如果您希望调用已隐藏在当前命名空间中的全局函数,请在函数前面使用::来绕过本地函数并调用全局函数。

答案 4 :(得分:0)

更正:以下文字基于g ++系列编译器的经验。在对答案的评论之后,我重新阅读了标准(其中指出ADL将用于常规名称查找,并且常规名称查找应该找到运算符&lt;&lt;)。我也尝试使用comeau编译器(我知道的最符合标准的编译器)并找到符号。这似乎是g ++的问题(尝试过版本3.3,4.1,4.3)。

原始答案:

搜索Koening查找(技术上ADL:依赖于参数的查找)。

简短的回答是,如果你有以下课程:

namespace test {
    class A {};
}

流插入运算符应定义为:

namespace test {
    std::ostream& operator<<( std::ostream&, A const & );
}

函数或运算符应该在与它所采用的参数之一相同的命名空间中定义。 (*)

当编译器找到函数调用时,例如:

namespace test2 {
   void g() {
      namespace1::class1 c1;
      namespace2::namespace3::class2 c2;
      f( c1, c2 );
   }
}

它会尝试在当前命名空间(在调用地点)或c1和c2类型(namespace1,namespace2 :: namespace3)的封闭命名空间中找到 f 函数,但它不会在搜索中尝试其他命名空间。

(*)在这种情况下,您几乎只限于 test 命名空间,因为您不允许向std命名空间添加函数(仅限模板特化)。

原帖的结尾

即使之前的评论可能仅仅是编译器的问题,但这是常见用法,并建议在与类型本身相同的命名空间中定义对用户定义类型进行操作的所有自由函数。

答案 5 :(得分:0)

  

尝试显式调用该函数?

     

:: operator&lt;&lt;(cout,myObj);

是的,这确实有效!

  

它会尝试找到f函数   当前的命名空间(在...的位置)   调用)或封闭的命名空间   c1和c2类型(namespace1,   namespace2 :: namespace3),但它会   不要尝试其他命名空间   搜索范围。

那么让我们看看我是否做到了这一点:调用运算符的原因&lt;&lt;从main()函数工作是因为我在全局命名空间(就像运算符&lt;&lt;)。 从我实现的类调用时失败的原因是因为该类位于非全局命名空间中,并且没有变量将编译器指向全局命名空间。

答案 6 :(得分:0)

好的人要求提供具体的例子,所以这里是代码的相关部分。 //免责声明:在苗条的情况下,有人从我的大学看到这个,在提交文件中遇到它,并决定我复制它或什么,我的学号是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;
    }

答案 7 :(得分:0)

这对我有用

#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);
}