j_kubik
j_kubik

Reputation: 6181

Do c++ inline friend functions cause name hiding between namespaces?

Consider the following code:

namespace Foo1 {
void add( int ) {}
void subtract( int ) {}
}

namespace Foo2 {


    class Bar {
    public:
        friend void add( Bar ) {}
    };

    void subtract( Bar ) {}

    void xxx() {
        int i = 0;
        using namespace Foo1;
        add( i );  // Is this an error or not?
        subtract( i ); // This is an error due to name hiding
    }
}

In the Foo2::xxx() I used the using namespace to be able to access both Foo1::add and Foo1::subtract. The call to the subtract is obviously an error because the Foo2::subtract hides the name. But the Foo2::add should not really be visible in Foo2 as it can only be found using ADL and it should not hide the Foo1::add. Is my understanding correct?

I have tried the above code on multiple versions of MSVC and gcc. The former has consistently rejected the add(i) call but error messages were not clear to me. The latter has consistently accepted it. Which of these (if any) is correct?

Upvotes: 3

Views: 113

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 311068

As already was pointed to (The C++ 20 Standard, 9.7.1.2 Namespace member definitions)

3 If a friend declaration in a non-local class first declares a class, function, class template or function template100 the friend is a member of the innermost enclosing namespace.

So this friend function add is a member of the namespace Foo2. However it is invisible in the namespace Foo2 until a corresponding function declaration appears in the namespace Foo2. And can be found only due to the argument-dependent look-up.

namespace Foo2 {

    class Bar {
    public:
        friend void add( Bar ) {}
    };
    //..

The name add from the namespace Foo1 would be hidden if you write before the function xxx

namespace Foo2 {

    class Bar {
    public:
        friend void add( Bar ) {}
    };

    void add( Bar );
    //...

That is if the friend function add will be redeclared in the enclosing namespace.

Within the function xxx

void xxx() {
    int i = 0;
    using namespace Foo1;
    add( i );  // Is this an error or not?
    subtract( i ); // This is an error due to name hiding
}

the compiler considers the name substract in the following order. At first it looks through the enclosing namespace that is the namespace Foo2. And this namespace has the declared name substract. So the process of searching stops. The overloaded function void substract( int ) is not found because due to the using directive it is considered as a member of the global namespace. That is of the namespace that enclose the namespace specified in the using directive and the namespace that contains the using directive.

You can consider it the following way (due to the using directibe)

// the global namespace

void subtract( int ) {}

namespace Foo2
{
    class Bar {
    public:
        friend void add( Bar ) {}
    };

    void subtract( Bar ) {}

    void xxx() {
        int i = 0;
        // using namespace Foo1;
        add( i );  // Is this an error or not?
        subtract( i ); // This is an error due to name hiding
    }
}

Instead of the using directive you could use a using declarations to make the both functions from the namespace Foo1 visible in the function xxx.

For example

void xxx() {
    int i = 0;
    using Foo1::subtract, Foo1::add;
    add( i );  // Is this an error or not?
    subtract( i ); // This is an error due to name hiding
}

Upvotes: 0

I think GCC is right here.

[namespace.memdef] (emphasis mine)

3 If a friend declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup ([basic.lookup.unqual]) or qualified lookup ([basic.lookup.qual]). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship).  — end note ] If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments ([basic.lookup.argdep]).

As such, the unqualified add( i ) should not by itself find the declaration of add( Bar ), which means lookup should continue and consider the names brought in by the using directive. And since the argument is not of a class type, ADL is out of the question. I'd conclude that add( Bar ) should not hide add( int ).

Upvotes: 5

Related Questions