ADL fails for friend functions...

W

Werner

Hi All,

Here follows a minimal example that fails to compile:

namespace n1{
class X1
{
friend void join( X1&, X1& ){ }
};

}
class Y
{
void join(){}
void foo()
{
struct{ void operator()( n1::X1&, n1::X1& ){ } }join_x;
n1::X1 xa, xb;
join( xa, xb );
}
};

The friend function join is not found because ADL is
"negated" by the Y::join.

Is there any other way to call join defined in X from within foo?
Apart
from moving the call outside the scope of Y (see next example)?

namespace n1{
class X1
{
friend void join( X1&, X1& ){ }
};

}
class Y
{
void join(){}
friend void join_x( n1::X1&, n1::X1& );
void foo();
};

void join_x( n1::X1& xa, n1::X1& xb )
{
join( xa, xb );
}

void Y::foo()
{
n1::X1 xa, xb;
join_x( xa, xb );
}

The reason I'm asking this question is because of Andrei
Alexandrescu's
choice in Modern C++ Design to use friend functions instead of member
functions for reset (compared to boost that does the opposite). I
realize
his rationale is/was to prevent accidental calling of reset for the
contained
type...

Regards,

Werner
 
E

Edek

Hi All,

Here follows a minimal example that fails to compile:

namespace n1{
class X1
{
friend void join( X1&, X1& ){ }
};

You need to bring join out to make ADL consider it

namespace n1{
class X1
{
friend void join( X1&, X1& );
};

void join (X1&, X1&) {};
}
.... and ...
}
class Y
{
void join(){}
void foo()
{
struct{ void operator()( n1::X1&, n1::X1& ){ } }join_x;
n1::X1 xa, xb;
join( xa, xb );
}
};

class Y
{
void join(){}
void foo()
{
struct{ void operator()( n1::X1&, n1::X1& ){ } }join_x;
n1::X1 xa, xb;

using ::n1::join;
join( xa, xb );
}
};

Local scope has preference in the standard: if there is a local
symbol, other lookup methods are not attempted, unless you bring
it in with "using". (I am using non-formal language, see
the standard on "using" directive). Only then regular overload
matching kicks in.
The friend function join is not found because ADL is
"negated" by the Y::join.

Is there any other way to call join defined in X from within foo?
Apart
from moving the call outside the scope of Y (see next example)?

The above compiles with gcc and is consistent with what I know.
The reason I'm asking this question is because of Andrei
Alexandrescu's
choice in Modern C++ Design to use friend functions instead of member
functions for reset (compared to boost that does the opposite). I
realize
his rationale is/was to prevent accidental calling of reset for the
contained
type...

Could you quote directly, I am confused with what you wrote, it does not
fit anything I can come up with.

Edek
 
W

Werner

You need to bring join out to make ADL consider it

namespace n1{
class X1
{
   friend void join( X1&, X1& );

};

void join (X1&, X1&) {};}

Yes, I know you can define it outside the class for it to be
found in the namespace (by using), but apart from that?
Local scope has preference in the standard: if there is a local
symbol, other lookup methods are not attempted, unless you bring
it in with "using". (I am using non-formal language, see
the standard on "using" directive). Only then regular overload
matching kicks in.

My apologies, I was wanting my first example to look like this:

namespace n1{
class X1
{
friend void join( X1&, X1& ){ }
};
}

class Y
{
void join(){}
void foo()
{
n1::X1 xa, xb;
join( xa, xb );
}
};

I've deliberately not defined the friend outside. The question
still stands - can you call join in any other way than by
removing Y's join from the overload set?
Could you quote directly, I am confused with what you wrote, it does not
fit anything I can come up with.

Quote Modern C++ Design Ch 7.4:

"However, experience has proven that member functions are not very
suitable for smart pointers. The reason is that the interaction
between member function calls for the smart pointer for the
pointed-to object can be extremely confusing."

I've noticed that in Loki he has, depending on compiler either
defined the friends in the class or in the namespace...

For this reason (ADL not kicking in automatically if overload
exists) I'm beginning to think that this was boosts choice for using
members instead of non-members in their smart pointers.

Kind regards,

Werner
 
E

Edek

I've deliberately not defined the friend outside. The question
still stands - can you call join in any other way than by
removing Y's join from the overload set?

"Using" does not remove Y::join from overload set. It adds n1::join.
Quote Modern C++ Design Ch 7.4:

"However, experience has proven that member functions are not very
suitable for smart pointers. The reason is that the interaction
between member function calls for the smart pointer for the
pointed-to object can be extremely confusing."

Ah, yes, I missed the simplest possible thing.

If my opinion matters at all, + has the same problem. You
"never know" if you are adding 1 to a pointer to int or
to the int-pointed-to. And if an Object has a parent Object,
you never know if it is the parent or the parent's parent.
Oh, well... I can live with that. Friend reset is imo worse,
can call a member of class where it is used by mistake,
or in a better case cause ambiguity, though VC sometimes
takes shortcuts here.
I've noticed that in Loki he has, depending on compiler either
defined the friends in the class or in the namespace...

For this reason (ADL not kicking in automatically if overload
exists) I'm beginning to think that this was boosts choice for using
members instead of non-members in their smart pointers.

Don't know. Maybe you can dig mail archives for discussions.

For me personally, the consequence of what I wrote above is that
the programmer does have to keep track of what is a pointer and
what is not a pointer. While the -> instead of . does happen
in my case sometimes (when I change a ref to a ptr or the opposite)
I have never hit the described problem. I wonder only if
I become to hit this problem since I started using auto a lot.
Still, member seems more natural to me, I find it more readable.
Also note that reset(whatever) will match reset(T) if whatever is
convertible to T and there is no preferred alternative and
reset is not a member.

Edek
 
V

Victor Bazarov

[..]
namespace n1{
class X1
{
friend void join( X1&, X1& ){ }
};
}
[..]
I've deliberately not defined the friend outside. The question
still stands - can you call join in any other way than by
removing Y's join from the overload set?

This rule comes to mind: the name of a friend function defined in the
class definition shall *not* be inserted into the namespace scope.
Don't remember where in the Standard it is, or whether it has changed
over the years... But for some reason I seem to remember that it
exists, and think that it applies here.

V
 

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

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top