在命名空间中重载的ostream operator << 隐藏了其他ostream :: operator。

3

使用gcc版本5.2.0(GCC)并使用--std=c++14,如果取消注释MyNamespace命名空间中的operator ostream,以下代码将不再编译通过。这是一个错误还是特性?(使用g++ -c --std=c++14 x.cxx进行编译)

#include <string>
#include <iostream>

typedef std::pair<std::string, std::string> StringPair;

std::ostream& operator<<( std::ostream&, const StringPair &pair) {
  std::cout <<pair.first<<"."<<pair.second;
}

namespace MyNamespace {
  class MyClass {};
  //std::ostream& operator<< (std::ostream&, const MyClass &);
  void xxx ();
}

void MyNamespace::xxx () {
  StringPair pair;pair.first="1";pair.second="2";
  std::cout <<pair<<std::endl;
}

如果我不注释运算符<<,则会得到以下错误消息:

x.cxx: In function ‘void MyNamespace::xxx()’:
x.cxx:18:13: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘StringPair {aka std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >}’)
std::cout <<pair<<std::endl;
         ^

首先,您可能需要在代码中删除未定义行为,需要从接受pair的全局operator<<函数返回一些内容。并且您可能应该使用作为参数传递的流,而不是硬编码std::cout。这并不能解决您的问题,但是无关的问题往往会隐藏实际的问题。 - Some programmer dude
@Someprogrammerdude 我刚刚尝试并重现了报告的错误。我不认为未定义的行为与此有任何关系 - 这是在编译期间发生的。 - Ivaylo Strandjev
重现行为的ideone链接:https://ideone.com/Lgg5oL - Ivaylo Strandjev
这一定是与ADL有关的问题,但我就是看不出来为什么。请注意,VS2017也存在此问题,不仅限于GCC。 - Some programmer dude
也不仅限于使用std=c++14的GCC。我也不知道发生了什么。 - Ivaylo Strandjev
显示剩余5条评论
1个回答

3
此处所述,这是一个名称隐藏的例子。通过在MyNamespace命名空间中定义operator<<,所有来自更高级别的命名空间(如全局)的定义都被隐藏。
请注意,如此处所述:

[...]这个特性不会干扰Koenig查找[...],因此来自std::的IO操作符仍然可以被找到。

(有关Koenig查找的详细信息,请参见此处
解决方案是使用using指令引用其他命名空间中的重载,如此处此处所述。这是由Michael Nastenko在评论中提到的。
因此,using ::operator<<;,其中::指全局命名空间。
因此,代码将变为:
#include <string>
#include <iostream>

typedef std::pair<std::string, std::string> StringPair;

std::ostream& operator<<(std::ostream& os, const StringPair &pair) {
    os << pair.first << '.' << pair.second;
    return os;
}

namespace MyNamespace {
    class MyClass {};
    using ::operator<<;
    std::ostream& operator<< (std::ostream&, const MyClass &);
    void xxx();
}

void MyNamespace::xxx() {
    StringPair pair("1","2");
    std::cout<<pair<<std::endl;
}

int main() {
    MyNamespace::xxx();
    return 0;
}

example on Coliru


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接