11

Minimal example:

class A { friend void swap(A& first, A& second) {} void swap(A& other) {} void call_swap(A& other) { swap(*this, other); } }; int main() { return 0; } 

g++ 4.7 says:

friend.cpp: In member function ‘void A::call_swap(A&)’: friend.cpp:7:20: error: no matching function for call to ‘A::swap(A&, A&)’ friend.cpp:7:20: note: candidate is: friend.cpp:4:7: note: void A::swap(A&) friend.cpp:4:7: note: candidate expects 1 argument, 2 provided 

Outcomment line 4:

// void swap(A& other) {} 

...and it works fine. Why, and how to fix this, if I want to keep both variants of my swap function?

5
  • This looks like a compiler bug to me? Commented Aug 26, 2013 at 17:44
  • @McKay: might be. However, clang gives me the same error (with different explenation). Commented Aug 26, 2013 at 17:52
  • How about defining the function outside the class in the global namespace? Commented Aug 26, 2013 at 17:53
  • @UchiaItachi: wow, does not work either! Seems like friend is not the problem here? Commented Aug 26, 2013 at 17:55
  • 3
    You ran afoul of C++ trying to be clever and not impose that member methods (and variables) only be accessible after this->. It's a convenient syntax trick, certainly, but as a result in order to avoid unknowingly calling non-member functions the name lookup rules specify that the class members hide the surrounding names. While it is reasonable for variables (similar to nested scopes), for functions it breaks expectations of proper overload resolution :/ Commented Aug 26, 2013 at 18:10

6 Answers 6

8

I believe it is because the compiler is trying to find the function within the class. This should be a minimalistic change to make it work (it works in Visual Studio 2012):

class A; // this and the next line are not needed in VS2012, but void swap(A& first, A& second); // will make the code compile in g++ and clang++ class A { friend void swap(A& first, A& second) {} void swap(A& other) {} void call_swap(A& other) { ::swap(*this, other); // note the scope operator } }; int main() { return 0; } 
Sign up to request clarification or add additional context in comments.

4 Comments

No, this shouldn't work. Clang++ doesn't accept it and the Standard says: "A name prefixed by the unary scope operator :: is looked up in global scope, in the translation unit where it is used. The name shall be declared in global namespace scope or shall be a name whose declaration is visible in global scope because of a using-directive" in [basic.lookup.qual]/4. Although the name of the friend function is in global scope, it hasn't been declared there.
It does compile in Visual Studio 2012. I put in a print statement within the friend definition and ran it to confirm that the friend function does get executed and it did.
I don't doubt that it works in VS2012. I doubt that it should work, meaning that it's either a bug in the VC++ compiler or at least a difference in interpreting the Standard. The OP uses g++ which doesn't accept it either.
Thanks, understood. Having just a declaration outside makes the code compile and work on g++ and clang++: coliru.stacked-crooked.com/…
7

As a workaround, you can declare a static version of swap. Then, you can declare the friend version to call the static version.

class A { public: friend void swap(A& first, A& second) { A::swap(first, second); } private: static void swap(A& first, A& second) {} void swap(A& other) {} void call_swap(A& other) { swap(*this, other); } }; int main () { A a, b; swap(a, b); } 

Comments

7

Why

Inside the class, names scoped within the class hide those in the surrounding namespace; so the friend (whose name is scoped in the namespace, but not directly accessible there) is hidden by the member (scoped in the class) and not available as a potential overload here. (Update: or perhaps it's a bit more complicated than that, as mentioned in the comments. The scope and name lookup rules are a bit hard to follow, especially when friends are involved).

how to fix this, if I want to keep both variants of my swap function?

There's no perfect solution. If the functions both do the same thing, then just use the member from other member functions. If you declare the friend outside the class definition, then it's accessible as ::swap; but this is a bit fragile if you put the class in a different namespace. (Update: or use a static member function as suggested by this answer; I didn't think of that).

7 Comments

I don't think the scope argument applies here: [class.friend]/7 "A friend function defined in a class is in the (lexical) scope of the class in which it is defined.". However, a class member found using unqualified lookup hides any name found using ADL as per [basic.lookup.argdep]/3
@DyP: Yes, you're probably right. I won't try to make my answer completely accurate, since my brain will probably melt if I try.
@DyP I'm almost certain all that means is that you are in class scope within the friend function definition, not at all that the friend function is added to the list of candidates for overload resolution. The friend function exists at an outer scope, and the member is the only function in the scope of another class member.
@MarkB You might be right, [class.friend]/6 says "A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope." Which probably doesn't mean iff it has namespace scope, it can be defined inside the class. But then, it contradicts /7.
@DyP: The closest I got to a definition of lexical scope in the standard is the extended quote in 9.7/4 Like a member function, a friend function (11.3) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (9.4), but it has no special access rights to members of an enclosing class.
|
3

Keep to the standard swap idiom, and you won't have a problem:

void call_swap(A& other) { using std::swap; swap(*this, other); } 

Or use the Boost.Swap wrapper:

void call_swap(A& other) { boost::swap(*this, other); } 

This is pretty much equivalent to @Juan's solution, except you're not writing the helper yourself.

14 Comments

But that won't find the friend function either.
Yes, it will. It will find it with ADL.
@SebastianRedl Agreed, though for more difficult classes, this will not work.
@MikeSeymour The code is not like in the question. The code in question finds a member function, and thus suppresses ADL. This code finds a free function, and thus allows ADL, which will find the friend. That's why it works.
@DyP No, unqualified lookup stops the moment it finds results, so it stops at the using declaration and doesn't move on to class scope.
|
0

You can also use a helper function, as in this case

template<class T> void swap_elems(T& lhs, T& rhs) { using namespace std; swap(lhs, rhs); } class A { friend void swap(A& first, A& second) { first.swap(second); } public: void swap(A& other) {} void call_swap(A& other) { swap_elems(*this, other); } }; 

Comments

0

What you're observing here is that in absence of a previous declaration of a friend function, friendship within a class injects the name into the enclosing namespace, but NOT into the class scope. The only thing that happens at class scope is that the function named is granted access to private attributes.

This leaves only one swap function in the class scope (the member with one parameter) so that's the only candidate overload. Once you've found a candidate even if overload resolution fails you will never try another enclosing scope (shadowing).

If you really need both versions (and step back to make sure you do), put the implementation into a function like swap_impl which you call from the friend and the member.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.