警告定义在命名空间内声明的友元运算符

max*_*x66 7 c++ namespaces g++ operator-overloading friend

有人可以向我解释来自 g++ 的警告吗?

鉴于以下代码

#include <iostream>

namespace foo
 {
   struct bar
    { friend std::ostream & operator<< (std::ostream &, bar const &); };
 }

std::ostream & foo::operator<< (std::ostream & o, foo::bar const &)
 { return o; }

int main ()
 {
   foo::bar  fb;

   std::cout << fb;
 }
Run Code Online (Sandbox Code Playgroud)

我得到(来自 g++ (6.3.0) 但不是来自 clang++ (3.8.1) 而不是(感谢 Robert.M)来自 Visual Studio(2017 社区))这个警告

tmp_002-11,14,gcc,clang.cpp:10:16: warning: ‘std::ostream& foo::operator<<(std::ostream&, const foo::bar&)’ has not been declared within foo
 std::ostream & foo::operator<< (std::ostream & o, foo::bar const &)
                ^~~
tmp_002-11,14,gcc,clang.cpp:7:29: note: only here as a friend
     { friend std::ostream & operator<< (std::ostream &, bar const &); };
                             ^~~~~~~~
Run Code Online (Sandbox Code Playgroud)

我知道我可以如下定义运算符

namespace foo
 {
   std::ostream & operator<< (std::ostream & o, bar const &)
    { return o; }
 }
Run Code Online (Sandbox Code Playgroud)

但是...我的初始代码有什么问题?

n. *_* m. 6

考虑这个简单的程序:

namespace xxx { 
    struct foo {
       friend void bar();
    };
}

int main() {
    xxx::bar();
}
Run Code Online (Sandbox Code Playgroud)

你会得到一个编译错误(也有 clang),因为bar没有在命名空间中声明xxx

现在考虑一下:

namespace xxx {}
void xxx::bar() {}
Run Code Online (Sandbox Code Playgroud)

这也会由于同样的原因而失败,bar即未在名称空间中声明xxx

现在,当您将两者结合起来时,没有理由认为这种组合会突然变得合法。bar仍未在命名空间中声明xxx。然而 clang 允许这样做。这种行为不一致且令人困惑,最好将其描述为错误。


Ste*_*fan 5

nm的答案是正确的,虽然我需要更多的挖掘才能知道原因,所以这里有一些我遵循的链接:

CppCoreGuidelines 解释说“非成员运算符应该是朋友或在与其操作数相同的命名空间中定义”。这里更详细地解释了为什么使用相同的命名空间
也许这个来自 GCC 邮件列表的消息提供了更多的洞察力:看起来 GCC 人决定在 2016 年的某个时候更严格地处理这个问题。

这里的关键是命名空间。如果您的代码在命名空间foo 内而不是外部定义了operator<< ,那么您的代码就可以了,如下所示:

namespace foo
{
    struct bar
    { 
        friend std::ostream & operator<< (std::ostream &, bar const &);
    };

    // Implementation
    std::ostream & operator<< (std::ostream & o, foo::bar const &)
    { return o; }
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果您直接将实现与朋友定义放在一起,这会变得更简单,如this SO answer to a similar question所示

这是对一个非常相似的问题的另一个非常详细的 SO 答案。当我试图解决同样的问题时,它对我有所帮助(gcc 7 给了我这个警告,因为代码可以用更旧的版本编译得很好)。