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

时间:2017-06-25 19:37:15

标签: 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;
 }

我得到(来自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 &); };
                             ^~~~~~~~

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

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

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

3 个答案:

答案 0 :(得分:3)

n.m.的答案是正确的,虽然我需要更多的挖掘来解决它的原因,所以这里有一些链接:

CppCoreGuidelines解释了"Nonmember operators should be either friends or defined in the same namespace as their operands"。 以下是why the same namespace的更详细说明 也许这个this message from the GCC mailing list提供了更多的洞察力:看起来GCC人员决定在2016年的某个时候更严格地处理这个问题。

这里的关键是命名空间。如果代码在命名空间 foo 中而不是在外部定义了运算符&lt;&lt;&lt; ,那么您的代码就可以了,如下所示:

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

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

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

这是very similar question的另一个非常精细的答案。当我试图修复同样的问题时,它帮助了我(gcc 7给了我这个用于编译很好旧代版本的代码的警告)。

答案 1 :(得分:3)

考虑这个简单程序:

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

int main() {
    xxx::bar();
}

您将收到编译错误(也包含clang),因为bar未在命名空间xxx中声明。

现在考虑一下:

namespace xxx {}
void xxx::bar() {}

由于同样的生态原因,这也会失败,bar未在命名空间xxx中声明。

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

答案 2 :(得分:0)

我当时正在使用visual studio,所以我不确定它是否会为您解决问题,但我发现错误的唯一原因是您的运营商声明。 基本上它总是

        const type_name variable_id &;

Ampersand总是排在最后,const排在第一位,对我来说它现在正在运行。

您应该更改您的运营商声明并注明:

        friend std::ostream & operator<< (std::ostream &,const bar&);

在你的定义中更低:

std::ostream & foo::operator<< (std::ostream & o,  const foo::bar &)