2

我在 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 的 operator<< 完全在 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 中的一个类中。当我创建该类的一个实例并尝试打印其中一组时,它告诉我:错误 C2679:二进制“<<”:未找到采用“const MAIN-NAMESPACE”类型的右侧操作数的运算符: :POINTER-COLLECTIONS-NAMESPACE::Set' (或没有可接受的转换)

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

知道有什么问题吗?(注意:我尝试了 using 和 include 我能想到的任何组合)。

4

8 回答 8

3

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

我唯一能想到的是,具有相同名称的声明 inMAIN-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);
  }
}
于 2009-01-30T00:48:27.987 回答
3

好的,我想通了。jpalecek 关于命名空间中存在另一个 operator<< 的直觉是正确的(显然我忘了将其注释掉)。

命名空间的查找规则首先在函数调用的命名空间中开始搜索,然后搜索封闭的命名空间,直到全局命名空间(如果没有找到匹配项,它会执行依赖于参数的查找)。但是,如果一路上它找到了一些与 operator<< 的匹配项,它就会停止搜索,而不管这些函数中使用的类型可能不兼容,就像这里的情况一样。

解决方案是将其包含到 MAIN-NAMESPACE 中(我不允许这样做),或者使用“使用 ::operator<<”从全局命名空间中导入它。

于 2009-01-30T13:38:47.277 回答
0

尝试显式调用该函数?

::operator<<( cout, myObj );
于 2009-01-30T00:23:32.187 回答
0

正如 SoaBox 指出的那样,尝试显式调用它。

供您参考,如果您希望调用已隐藏在当前命名空间中的全局函数,请在函数前加上 :: 以绕过局部函数并调用全局函数。

于 2009-01-30T00:31:42.670 回答
0

更正:以下文本基于 g++ 系列编译器的经验。在对答案发表评论后,我重新阅读了标准(其中指出 ADL 将用于常规名称查找,并且常规名称查找应该找到运算符<<)。我也尝试过使用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 类型的封闭命名空间(命名空间 1、命名空间 2::命名空间 3)中查找f函数,但不会在搜索中尝试其他命名空间。

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

原帖结束

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

于 2009-01-30T00:39:13.793 回答
0

尝试显式调用该函数?

::operator<<( cout, myObj );

是的,这确实有效!

它将尝试在当前命名空间(调用位置)或 c1 和 c2 类型的封闭命名空间(命名空间 1、命名空间 2::命名空间 3)中查找 f 函数,但不会在搜索中尝试其他命名空间。

所以让我们看看我是否做对了:从 main() 函数调用 operator<< 的原因是因为我在全局命名空间中(就像 operator<<)。从我实现的类调用时失败的原因是该类位于非全局命名空间中,并且其中没有将编译器指向全局命名空间的变量。

于 2009-01-30T00:50:31.260 回答
0

好的人们要求一个具体的例子,所以这里是代码的相关部分。//免责声明:在slim case中,我大学的某个人看到了这个,在提交文件中遇到了它,并决定我复制了它什么的,我的学号是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;
    }
于 2009-01-30T01:07:20.337 回答
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);
}
于 2009-01-30T01:26:55.743 回答