【问题标题】:warning defining friend operator declared inside a namespace在命名空间内声明的定义友元运算符的警告
【发布时间】:2017-11-28 17:06:26
【问题描述】:

有人可以向我解释来自 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) 而不是来自 Visual Studio(2017 社区)的(感谢 Robert.M))这个警告

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

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

【问题讨论】:

  • 您是否找到了解决方案或评论了为什么会这样?还有,你当初为什么把朋友放在那里?这有必要工作吗?
  • @Ilendir - 目前没有找到“解决方案”(但我不寻找“解决方案”;我要求解释); (原始的,更复杂的)代码的第一个版本不是我的,而是来自开源代码;我建议按照第二个块(namespace foo 块内的运算符)中显示的方式修改代码,但我的疑问仍然存在:为什么第一个是错误的(或危险的)? Robert.M 的回答是错误的。

标签: c++ namespaces g++ operator-overloading friend


【解决方案1】:

考虑这个简单的程序:

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】:

    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 而不是在外部定义 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; }
    }
    

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

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

    【讨论】:

      【解决方案3】:

      好吧,我遇到了同样的问题。不过我找到了解决方法:在命名空间范围内添加另一个函数声明。

      就像这样:

      namespace foo {
          class A {
          public:
              friend std::ifstream &operator >> (std::ifstream &in, A &a);
          };
      
          std::ifstream &operator >> (std::ifstream &in, A &a);
      }
      
      std::ifstream &foo::operator >> (std::ifstream &in, A &a) {
          [...]
          return in;
      }
      

      【讨论】:

        【解决方案4】:

        我使用的是 Visual Studio,所以我不确定它是否会为您解决问题,但我发现唯一错误的是您的操作员声明。 基本上总是这样

                const type_name variable_id &;
        

        & 总是在最后,而 const 在前,对我来说它现在正在运行。

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

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

        在你的定义中更低:

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

        【讨论】:

        • 感谢您的回答,但不,您错了:规则是 const 放在我们不会声明常量的部分的右侧(例如:int const * 声明一个可修改的指向一个常量整数的指针,其中int * const 声明一个指向可修改整数的常量指针)并且只有在const 的左侧没有任何内容时,它才会应用于右侧的部分(所以const int * 是指向常量整数的可修改指针,与int const * 完全相同。所以const bar &amp;bar const &amp; 完全一样。
        • 但是......对不起......使用Visual Studio,您会收到警告,编译我的代码示例,还是不行?
        • 是的,0 个错误,0 个警告,我忘了说,同样的事情必须在运算符的定义中做得更低。
        • 谢谢;另一个问题:Visual Studio 的版本?
        • 2017 社区,真奇怪,它有什么不同。
        猜你喜欢
        • 2013-05-19
        • 2012-01-01
        • 2020-12-03
        • 1970-01-01
        • 2020-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多