Compiler error with friend


A

Adrian

Hi Guys,

I obviously have a mistake in this - I just cant seem to see it for
looking this morning.

I've tried various forms of the friend declaration but the compiler
seems to be ignoring them all.

Why wont the following compile when using namespaces

TIA

Adrian

[email protected]:~> g++ -Wall -ansi -pedantic -Wextra -Weffc++ foo.cc
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'

[email protected]:~> g++ -DNAMESPACES -Wall -ansi -pedantic -Wextra
-Weffc++ foo.cc
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'
foo.cc: In function 'std::eek:stream& operator<<(std::eek:stream&, const
level1::level2::foo&)':
foo.cc:18:12: error: 'void level1::level2::foo::print(std::eek:stream&)
const' is private
foo.cc:43:16: error: within this context



#include <iostream>

#ifdef NAMESPACES
namespace level1
{
namespace level2
{
#endif
class foo
{
public:
#ifdef NAMESPACES
friend std::eek:stream &operator<<(std::eek:stream &os, const
::level1::level2::foo &rhs);
#else
friend std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs);
#endif
private:
void print(std::eek:stream &os) const
{
}
};
#ifdef NAMESPACES
}}
#endif

#ifdef NAMESPACES
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs);
#else
std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs);
#endif

int main(int argc, char *argv[])
{
return 0;
}

#ifdef NAMESPACES
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs)
#else
std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs)
#endif
{
rhs.print(os);
return os;
}
 
Ad

Advertisements

K

Kevin P. Fleming

Hi Guys,

I obviously have a mistake in this - I just cant seem to see it for
looking this morning.

I've tried various forms of the friend declaration but the compiler
seems to be ignoring them all.

Why wont the following compile when using namespaces

TIA

Adrian

[email protected]:~> g++ -Wall -ansi -pedantic -Wextra -Weffc++ foo.cc
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'

[email protected]:~> g++ -DNAMESPACES -Wall -ansi -pedantic -Wextra
-Weffc++ foo.cc
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'
foo.cc: In function 'std::eek:stream& operator<<(std::eek:stream&, const
level1::level2::foo&)':
foo.cc:18:12: error: 'void level1::level2::foo::print(std::eek:stream&)
const' is private
foo.cc:43:16: error: within this context



#include <iostream>

#ifdef NAMESPACES
namespace level1
{
namespace level2
{
#endif
class foo
{
public:
#ifdef NAMESPACES
friend std::eek:stream &operator<<(std::eek:stream &os, const
::level1::level2::foo &rhs);

This declaration of 'operator<<' is in the level1::level2 namespace. If
you want it to be in the anonymous namespace, you'll have to declare it
as 'friend std::eek:stream& ::eek:perator<<(...)'.
#else
friend std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs);
#endif
private:
void print(std::eek:stream &os) const
{
}
};
#ifdef NAMESPACES
}}
#endif

#ifdef NAMESPACES
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs);
#else
std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs);
#endif

int main(int argc, char *argv[])
{
return 0;
}

#ifdef NAMESPACES
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs)
#else
std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs)
#endif
{
rhs.print(os);
return os;
}
 
V

Victor Bazarov

Hi Guys,

I obviously have a mistake in this - I just cant seem to see it for
looking this morning.

I've tried various forms of the friend declaration but the compiler
seems to be ignoring them all.

Why wont the following compile when using namespaces

TIA

Adrian

[email protected]:~> g++ -Wall -ansi -pedantic -Wextra -Weffc++ foo.cc
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'

[email protected]:~> g++ -DNAMESPACES -Wall -ansi -pedantic -Wextra
-Weffc++ foo.cc
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'
foo.cc: In function 'std::eek:stream& operator<<(std::eek:stream&, const
level1::level2::foo&)':
foo.cc:18:12: error: 'void level1::level2::foo::print(std::eek:stream&)
const' is private
foo.cc:43:16: error: within this context



#include <iostream>

#ifdef NAMESPACES
namespace level1
{
namespace level2
{
#endif
class foo
{
public:
#ifdef NAMESPACES
friend std::eek:stream &operator<<(std::eek:stream &os, const
::level1::level2::foo &rhs);
#else
friend std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs);
#endif

You're going to avoid many problems if you declare those functions where
they are supposed to reside *before* using their name in a friend
declaration. Try it. Before your present 'namespace level1' write the
function "prototype":

std::eek:stream &operator<<(std::eek:stream &os,
const level1::level2::foo &rhs);

As you can see, you will need the have 'foo' declared as well. In order
to do that, *before* the function declaration, write

namespace level1 { namespace level2 { class foo; } }

which will declare that 'foo' is a class inside those namespaces. That
should take care of it.

Namespaces can be re-opened in a translation unit (or even across them).
The forward-declaration of 'foo' will allow the compiler to create a
function declaration of the proper type. The 'friend' declaration then
will connect the class with the previously declared function. Then,
when you finally define the function, the compiler will know that you meant.
private:
void print(std::eek:stream &os) const
{
}
};
#ifdef NAMESPACES
}}
#endif

#ifdef NAMESPACES
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs);
#else
std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs);
#endif

int main(int argc, char *argv[])
{
return 0;
}

#ifdef NAMESPACES
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs)
#else
std::eek:stream &operator<<(std::eek:stream &os, const foo &rhs)
#endif
{
rhs.print(os);
return os;
}

V
 
A

Adrian

This declaration of 'operator<<' is in the level1::level2 namespace. If
you want it to be in the anonymous namespace, you'll have to declare it
as 'friend std::eek:stream& ::eek:perator<<(...)'.
Hi Keven,

Tried that as well. Different error - but didnt work. :-(

12 #ifdef NAMESPACES
13 friend std::eek:stream &::eek:perator<<(std::eek:stream &os, const
::level1::level2::foo &rhs);
14 #else

[email protected]:~> g++ -DNAMESPACES -Wall -ansi -pedantic -Wextra
-Weffc++ foo.cc
foo.cc:13:91: error: 'std::eek:stream& operator<<(std::eek:stream&, const
level1::level2::foo&)' should have been declared inside '::'
foo.cc:18:12: warning: unused parameter 'os'
foo.cc:32:5: warning: unused parameter 'argc'
foo.cc:32:5: warning: unused parameter 'argv'
 
A

Adrian

Thanks Guys, got it now with combination of forward decl and marking
operator<< as global scope. So basically a combination of Victor's and
Kevin's replies.



#include <iostream>

namespace level1 {namespace level2 { class foo; }}
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs);

namespace level1
{
namespace level2
{
class foo
{
public:
friend std::eek:stream &::eek:perator<<(std::eek:stream &os, const foo &rhs);
private:
void print(std::eek:stream &os) const
{
}
};
}}



int main(int argc, char *argv[])
{
return 0;
}

std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo &rhs)
{
rhs.print(os);
return os;
}
 
U

Ulrich Eckhardt

Adrian said:
Thanks Guys, got it now with combination of forward decl and marking
operator<< as global scope. So basically a combination of Victor's and
Kevin's replies.

Actually, Leigh's suggestion was the best of the three.
namespace level1 {namespace level2 { class foo; }}
std::eek:stream &operator<<(std::eek:stream &os, const level1::level2::foo
&rhs);

namespace level1
{
namespace level2
{
class foo
{
public:
friend std::eek:stream &
::eek:perator<<(std::eek:stream &os, const foo &rhs);
private:
void print(std::eek:stream &os) const
{
}
};
}} [...]
std::eek:stream &
operator<<(std::eek:stream &os, const level1::level2::foo &rhs)
{
rhs.print(os);
return os;
}

Actually, I'd like to ask why you want the operator<< overload to be in
the global namespace? There is no need for this due to ADL (Argument-
Dependent Lookup aka Koenig Lookup), which basically means it will be
found even if it is in the namespace, just by the parameters given to it.

Additionally, you can declare and define the operator inline, which saves
quite a bit of typing. Since it also is very short, it doesn't hurt that
it is inline either.

Uli
 
Ad

Advertisements

J

James Kanze

On 13/01/2011 15:26, Adrian wrote:

[...]
Use friend name injection; that should solve your problem
(there is no need to define operator<< at global scope due to
ADL).

I'm not sure about your vocabulary here. What do you mean by
"friend name injection"? The names of a friend used to be
injected into the surrounding scope, but this was removed from
the standard. ADL does do a sort of injection, but this is
limited. (It will work in the case of operator<<. But it can
fail to work in some other special cases. ADT only kicks in
once the compiler has found a function. ADT will add to the
overload set, but it won't be used if the initial overload set
is empty.)
 
J

James Kanze

On 13/01/2011 15:26, Adrian wrote:
[...]
Use friend name injection; that should solve your problem
(there is no need to define operator<< at global scope due to
ADL).
I'm not sure about your vocabulary here. What do you mean by
"friend name injection"? The names of a friend used to be
injected into the surrounding scope, but this was removed from
the standard. ADL does do a sort of injection, but this is
limited. (It will work in the case of operator<<. But it can
fail to work in some other special cases. ADT only kicks in
once the compiler has found a function. ADT will add to the
overload set, but it won't be used if the initial overload set
is empty.)
struct foo
{
friend void friend_name_injection(const foo& a) { std::cout <<
a.iPrivateBits; }
public:
foo() : iPrivateBits(42) {}
private:
int iPrivateBits;
};
int main()
{
foo o;
friend_name_injection(o);
}

This doesn't use friend name injection, but ADL. And while this
particular example does seem to work, I had a problem recently
with something very similar, which failed to compile with g++.
(But looking at it, my case was slightly different.)

Friend name injection, as defined in the ARM and other
prestandard documents, would have resulted in
friend_name_injection being found even without ADL (which didn't
exist back then); a friend declaration always injected the name
into the surrounding scope (or back then, in pre namespace days,
file scope).

There's an issue I'm not too sure about (although I know what
compilers actually do, and I don't expect that to change). ADL
is only used for the postfix-expression of a function call. But
the meaning of () is context dependent. Given the expression
friend_name_injection(o), above, how does the compiler decide
whether the () is a function call, or part of an "Explicit type
conversion (functional notation)"? The obvious answer is that
it is a function call, because friend_name_injection is the name
of a function. The compiler cannot find friend_name_injection
(to know that it is function, and not the name of a type) until
it uses ADL, and it cannot use ADL until it knows that
friend_name_injection is not the name of a type.

I think I'll raise the issue in comp.std.c++. Given the
expression `f(a)', all of the compilers I know assume a function
call until proven otherwise, but I can't find anything in the
standard to support this.
Are you claiming that the above code will become illegal in
C++0x? If not then how can it be that friend name injection
is being removed?

No, but the reason it works is ADL, and not friend name
injection.
 
K

Kevin P. Fleming

On 14/01/2011 09:07, James Kanze wrote:
On 13/01/2011 19:38, James Kanze wrote:
On 13/01/2011 15:26, Adrian wrote:

[...]
Use friend name injection; that should solve your problem
(there is no need to define operator<< at global scope due to
ADL).

I'm not sure about your vocabulary here. What do you mean by
"friend name injection"? The names of a friend used to be
injected into the surrounding scope, but this was removed from
the standard. ADL does do a sort of injection, but this is
limited. (It will work in the case of operator<<. But it can
fail to work in some other special cases. ADT only kicks in
once the compiler has found a function. ADT will add to the
overload set, but it won't be used if the initial overload set
is empty.)

struct foo
{
friend void friend_name_injection(const foo& a) { std::cout<<
a.iPrivateBits; }
public:
foo() : iPrivateBits(42) {}
private:
int iPrivateBits;
};

int main()
{
foo o;
friend_name_injection(o);
}

This doesn't use friend name injection, but ADL. And while this
particular example does seem to work, I had a problem recently
with something very similar, which failed to compile with g++.
(But looking at it, my case was slightly different.)

Friend name injection, as defined in the ARM and other
prestandard documents, would have resulted in
friend_name_injection being found even without ADL (which didn't
exist back then); a friend declaration always injected the name
into the surrounding scope (or back then, in pre namespace days,
file scope).

There's an issue I'm not too sure about (although I know what
compilers actually do, and I don't expect that to change). ADL
is only used for the postfix-expression of a function call. But
the meaning of () is context dependent. Given the expression
friend_name_injection(o), above, how does the compiler decide
whether the () is a function call, or part of an "Explicit type
conversion (functional notation)"? The obvious answer is that
it is a function call, because friend_name_injection is the name
of a function. The compiler cannot find friend_name_injection
(to know that it is function, and not the name of a type) until
it uses ADL, and it cannot use ADL until it knows that
friend_name_injection is not the name of a type.

I think I'll raise the issue in comp.std.c++. Given the
expression `f(a)', all of the compilers I know assume a function
call until proven otherwise, but I can't find anything in the
standard to support this.

Are you claiming that the above code will become illegal in
C++0x? If not then how can it be that friend name injection
is being removed?

No, but the reason it works is ADL, and not friend name
injection.


Why can't you just admit when you are wrong? The code I gave is an
example of friend name injection; from Wikipedia:

"a C++ feature called "friend name injection", in which an in-class
declaration of a friend function made the function name visible in the
immediately surrounding namespace scope (possibly the global scope)."

The fact that ADL is being used does not mean that friend name injection
is not also happening. Friend name injection has not been removed.

I apologize; you are correct of course and I am wrong as the following
illegal code shows:

struct foo
{
friend void friend_name_injection() { std::cout << "friend name
injection"; }
public:
foo() : iPrivateBits(42) {}
private:
int iPrivateBits;
};

int main()
{
friend_name_injection(); // error
}

If injection was actually happening the error would not be an error.

Actually my correction was wrong; instead we were both initially
correct; *injection* is happening but the function is only accessible
via ADL:

struct foo
{
public:
friend void friend_name_injection() { std::cout << "friend name
injection"; }
public:
foo() : iPrivateBits(42) {}
private:
int iPrivateBits;
};

void friend_name_injection() // error, function already has a body.
{
}

That seems a bit strange... if the function can only be accessed through
ADL, then there's no value in injecting its name into the enclosing
namespace, is there?
 
J

James Kanze

On 14/01/2011 09:07, James Kanze wrote:

[...]
Why can't you just admit when you are wrong?

Because I'm not. I was actually there when freind name
injection was voted out.
The code I gave is an
example of friend name injection; from Wikipedia:
"a C++ feature called "friend name injection", in which an in-class
declaration of a friend function made the function name visible in the
immediately surrounding namespace scope (possibly the global scope)."

So the Wikipedia is wrong. (Nothing new there.) The reference
for C++ is not the Wikipedia, but the C++ standard. The only
injection defined in the C++ is class name injection: the name
of a class is injected into that class.
The fact that ADL is being used does not mean that friend name
injection is not also happening. Friend name injection has
not been removed.

It's certainly not present in the current (C++03) standard. It
wasn't present in C++98 either, although it was present in the
ARM (1989, I think).
 
J

James Kanze

On 14/01/2011 13:03, Leigh Johnston wrote:

[...]
Accepted.

[...]
Actually my correction was wrong; instead we were both initially
correct; *injection* is happening but the function is only accessible
via ADL:
struct foo
{
public:
friend void friend_name_injection() { std::cout << "friend name
injection"; }
public:
foo() : iPrivateBits(42) {}
private:
int iPrivateBits;

void friend_name_injection() // error, function already has a body.
{
}

That's not injection, at least not in the sense used for friend
name injection in the ARM, nor for class name injection in the
current standard. But you're right that strange things are
happening. The friend function is defined in the namespace,
even if its name has not been injected (and is not visible).
You can'd define it again, because of the one definition rule;
it does exist, even if its name is not visible.
 
Ad

Advertisements

J

James Kanze

On 14/01/2011 13:03, Leigh Johnston wrote:
[...]
That's not injection, at least not in the sense used for friend
name injection in the ARM, nor for class name injection in the
current standard. But you're right that strange things are
happening. The friend function is defined in the namespace,
even if its name has not been injected (and is not visible).
You can'd define it again, because of the one definition rule;
it does exist, even if its name is not visible.
So what term would you use for this if not "friend name injection"?
"Friend definition" as opposed to "friend declaration" perhaps?

That's a good question. I don't know. The standard doesn't use
any name for it; it just says that the name will be found in
this context, and not in some other context. In the discussions
in the standards committee, name injection means that the name
will be found in all contexts in which name lookup includes that
scope. In this case, we have:

-- The name will be found in ADL, but not in other name
lookups. (In ADL, it will be found "as if" name injection
had occured.)

-- The name is defined in the given scope, even if it isn't
visible. I don't think this is a real problem (unlike the
issue of ADL); whether something is defined or not is more
or less independent of visibility. (If I define a function
f() in global scope a.cpp, it isn't visible in other
translation units, but it is still defined in global scope,
and a second definition violates the one definition rule.)

I really don't know how to describe the first case. Conditional
name injection would seem a valid description to me, even if the
standard doesn't talk about name injection.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top