warning defining friend operator declared inside a namespace
Asked Answered
S

4

10

Someone can explain me a warning from g++ ?

Given the following code

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

I get (from g++ (6.3.0) but not from clang++ (3.8.1) and not (thanks Robert.M) from Visual Studio (2017 community)) this warning

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 &); };
                             ^~~~~~~~

I know that I can define the operator as follows

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

but... what's wrong with my initial code ?

Sarong answered 25/6, 2017 at 19:37 Comment(3)
This should help #3891902Hophead
Have you found a solution yet or a comment why this is the case? also, why did you put friend up there in the first place? is this necessary to work?Impressive
@Impressive - no "solution" found at the moment (but I don't search for a "solution"; I ask for an explanation); the first version of the (original, more complex) code isn't mine but is from an open source code; I've suggested to modify the code in the way showed in the second block (the operator inside a namespace foo block) but my doubt remain: why the first is wrong (or dangerous)? The answer from Robert.M is wrong.Sarong
U
7

Consider this simple program:

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

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

You will get a compilation error (with clang too) because bar is not declared in namespace xxx.

Now consider this:

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

This will also fail for the same exact reason, bar is not declared in namespace xxx.

Now when you combine the two, there's no reason why the combination should suddenly become legal. bar is still not declared in namespace xxx. Yet clang permits it. This behaviour is inconsistent and confusing, and is best described as a bug.

Unhesitating answered 24/8, 2017 at 16:21 Comment(0)
P
7

The answer of n.m. is right, although I needed a bit more digging to get it why, so here are some links that I followed:

The CppCoreGuidelines explain that "Nonmember operators should be either friends or defined in the same namespace as their operands". Here's a more detailed explanation of why the same namespace.
Maybe this this message from the GCC mailing list provides even more insight: it looks as if the GCC people decided to handle this issue more strictly some time im 2016.

The key here is the namespace. Your code would be OK if it defined the operator<< within the namespace foo instead of outside, like this:

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

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

Note that this gets simpler, if you put the implementation directly with the friend definition, as shown in this SO answer to a similar question.

Here is another, quite elaborate SO answer to a very similar question. It helped me when I tried to fix the same problem (gcc 7 gave me this waring for code that compiled fine with much older versions).

Pecos answered 24/8, 2017 at 15:52 Comment(0)
U
7

Consider this simple program:

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

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

You will get a compilation error (with clang too) because bar is not declared in namespace xxx.

Now consider this:

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

This will also fail for the same exact reason, bar is not declared in namespace xxx.

Now when you combine the two, there's no reason why the combination should suddenly become legal. bar is still not declared in namespace xxx. Yet clang permits it. This behaviour is inconsistent and confusing, and is best described as a bug.

Unhesitating answered 24/8, 2017 at 16:21 Comment(0)
A
3

Well, I met the same problem. However I found a way to fix it: add another declaration of the function, within the namespace scope.

Just like this:

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;
}
Aegrotat answered 11/11, 2021 at 8:49 Comment(1)
oh thanks, that solves my problem too!! C++ is so difficult language, like a junglePlacia
M
0

I was using visual studio so I am not sure if it will solve a problem for you but the only thing I found wrong was a declaration of your operator. Basicly it is always

        const type_name variable_id &;

Ampersand always comes last and const comes first, for me it is running now.

You should change your operator declaration and to:

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

And lower in your definition:

std::ostream & foo::operator<< (std::ostream & o,  const foo::bar &)
Monty answered 25/6, 2017 at 20:24 Comment(6)
Thanks for the answer but no, you're wrong: the rule is that const is placed on the right of the part that we wont declare constant (by example: int const * declare a modifiable pointer to a constant integer where int * const declare a constant pointer to a modifiable integer) and only if there isn't nothing on the left of const, it is applied on the part in the right (so const int * is a modifiable pointer to a constant integer, exactly as int const *). So const bar & are exactly the same thing as bar const &.Sarong
But ... sorry... with visual studio you get the warning, compiling my code example, or nor?Sarong
Yes, 0 errors, 0 warnings, I forgot to mention that the same thing has to be done lower in the definition of the operator.Monty
Thanks; another question: version of Visual Studio?Sarong
2017 Community, starnge that it makes any difference.Monty
I don't think the version of Visual Studio make difference but I insert your experience in my question; thanks again,Sarong

© 2022 - 2024 — McMap. All rights reserved.