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?

Daniel Daranas
  • 22,454
  • 9
  • 63
  • 116
Johannes
  • 2,901
  • 5
  • 30
  • 50
  • This looks like a compiler bug to me? – McKay Aug 26 '13 at 17:44
  • @McKay: might be. However, clang gives me the same error (with different explenation). – Johannes Aug 26 '13 at 17:52
  • How about defining the function outside the class in the global namespace? – Uchia Itachi Aug 26 '13 at 17:53
  • @UchiaItachi: wow, does not work either! Seems like `friend` is not the problem here? – Johannes Aug 26 '13 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 :/ – Matthieu M. Aug 26 '13 at 18:10

6 Answers6

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; }
user1952500
  • 6,611
  • 3
  • 24
  • 37
  • 1
    No, this shouldn't work. [Clang++ doesn't accept it](http://coliru.stacked-crooked.com/view?id=880bde810cebedd18bdf96d819a0b89c-25dabfc2c190f5ef027f31d968947336) 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. – dyp Aug 26 '13 at 19:31
  • 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. – user1952500 Aug 26 '13 at 19:34
  • 1
    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](http://coliru.stacked-crooked.com/view?id=880bde810cebedd18bdf96d819a0b89c-f674c1a6d04c632b71a62362c0ccfc51). – dyp Aug 26 '13 at 19:41
  • Thanks, understood. Having just a declaration outside makes the code compile and work on g++ and clang++: http://coliru.stacked-crooked.com/view?id=47a5c2955c824d70b9e7c7beb094bf11-f674c1a6d04c632b71a62362c0ccfc51 – user1952500 Aug 26 '13 at 19:55
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).

Community
  • 1
  • 1
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 4
    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 Aug 26 '13 at 18:03
  • @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. – Mike Seymour Aug 26 '13 at 18:10
  • 2
    @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. – Mark B Aug 26 '13 at 18:13
  • 1
    @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 Aug 26 '13 at 18:16
  • Ah, thanks! It works using '::swap' and declaring the function above the class. – Johannes Aug 26 '13 at 18:23
  • 1
    @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.* – David Rodríguez - dribeas Aug 26 '13 at 18:36
  • @DavidRodríguez-dribeas I guess it would've been clearer if it was "and the name of this function has namespace scope" in [class.friend]/7. – dyp Aug 26 '13 at 18:42
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);
}
jxh
  • 69,070
  • 8
  • 110
  • 193
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.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • But that won't find the friend function either. – Mike Seymour Aug 26 '13 at 18:18
  • Yes, it will. It will find it with ADL. – Sebastian Redl Aug 26 '13 at 18:19
  • @SebastianRedl Agreed, though for more difficult classes, this will not work. – Johannes Aug 26 '13 at 18:21
  • @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. – Sebastian Redl Aug 26 '13 at 18:23
  • @DyP Nope, no removing of any member function needed. ADL (once it happens) doesn't care at all about member functions. – Sebastian Redl Aug 26 '13 at 18:23
  • @Johannes It will work for absolutely all cases. The standard swap idiom is completely reliable, as long as you put your swap overloads in the right place, i.e. in the namespace of the class (declaring it as a friend inside works for that). – Sebastian Redl Aug 26 '13 at 18:24
  • Hmm it might work, but because of unqualified name lookup, not ADL. ADL is suppressed by [basic.lookup.argdep]/3. – dyp Aug 26 '13 at 18:26
  • @DyP What part of that? X is {using declaration for std::swap}, which matches none of the three suppression bullets, so Y is the ADL result, which is {friend swap(A&, A&)}. – Sebastian Redl Aug 26 '13 at 18:29
  • @MikeSeymour No, it will do overload resolution between `std::swap` (using declaration found via unqualified lookup) and `swap(A&, A&)` (found via ADL), and the latter will win because it's not a template. – Sebastian Redl Aug 26 '13 at 18:30
  • @SebastianRedl: Yes, I guess you're right. Sorry for the confusion; my brain tends to melt when I think too hard about name lookup. – Mike Seymour Aug 26 '13 at 18:31
  • Isn't the member function found also via unqualified lookup? (i.e. isn't this member function in *X*?) – dyp Aug 26 '13 at 18:33
  • 2
    @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. – Sebastian Redl Aug 26 '13 at 18:34
  • http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-C-1-of-n explains this stuff pretty well. – Sebastian Redl Aug 26 '13 at 18:41
  • @SebastianRedl: You are indeed right, it works in a more complicated example. Thanks! Though not that practicable to always write the `using` line every time. Is there a shortcut to that? (which does not use macros?) – Johannes Aug 26 '13 at 18:43
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);
    }
};
Jason
  • 409
  • 2
  • 7
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.

Mark B
  • 95,107
  • 10
  • 109
  • 188