2

I had an issue today where ADL wasn't finding a static member function for a type defined inside a class.

That is, in the below example, str(foo::Foo::Enum) isn't located via ADL without explicitly scoping it, foo::Foo::str(foo::Foo::Enum)

namespace foo {

struct Foo
{
    enum Enum
    {
        FOO1,
        FOO2
    };

    static const char* str(Enum e);
};

}

foo::Foo::Enum e = foo::Foo::FOO1;
const char* s = str(e);              // ADL doesn't work

I found this SO question, and as stated in the accepted answer, changing it to a friend function results in ADL now working.

namespace foo {

struct Foo
{
    enum Enum
    {
        FOO1,
        FOO2
    };

    friend const char* str(Enum e);  // note str is now a friend
};

}

foo::Foo::Enum e = foo::Foo::FOO1;
const char* s = str(e);              // ADL works now

Whilst this now helps ADL, I was surprised to find that I couldn't access str by scoping it with a namespace foo

foo::Foo::Enum e = foo::Foo::FOO1;
const char* s = foo::str(e);         // error: ‘str’ is not a member of ‘foo’

I ran a test, where I printed out the result of __PRETTY_FUNCTION__, and was even more surprised to see that the scope of str is apparently foo:::

__PRETTY_FUNCTION__: const char* foo::str(foo::Foo::Enum)

Working example below:

#include <iostream>

namespace foo {

struct Foo
{
    enum Enum
    {
        FOO1,
        FOO2
    };

    friend const char* str(Enum e)
    {
        return __PRETTY_FUNCTION__;
    }
};

}

int main()
{
    foo::Foo::Enum e = foo::Foo::FOO1;

    std::cout << str(e) << '\n';
    // std::cout << foo::str(e) << '\n'; // error: ‘str’ is not a member of ‘foo’

    return 0;
}

Output:

$ ./a.out
const char* foo::str(foo::Foo::Enum)

Question:

  • Why am I unable to locate str(..) explicitly scoping it with the enclosing namespace?
  • Why does __PRETTY_FUNCTION__ say it's in foo::, and yet I am unable to locate it as such?
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • 1
    There's a very big difference between the first and second example: In the first example the function `str` is still a *member of the class* `foo::Foo`, it's not a non-member function and can't be called as such. In the second example the `str` function *is* a non-member function. – Some programmer dude Dec 19 '17 at 14:50
  • @Someprogrammerdude yes, but why can't I call `foo::str`, since `str` is a friend function in the `foo` namespace? – Steve Lorimer Dec 19 '17 at 14:55
  • @Someprogrammerdude you'll notice I'm not trying to access `foo::Foo::str` (ie: as if it were a member), but instead I'm trying to access `foo::str` (ie: as if it were a function inside the `foo` namespace) – Steve Lorimer Dec 19 '17 at 15:00
  • If you define a `friend` non-member function *inline* it is really scoped inside the class. That `__PRETTY_FUNCTION__` doesn't say it is might be a problem with the compiler. – Some programmer dude Dec 19 '17 at 15:00
  • isn't the situation you ask is mentioned in the answer you just linked? – apple apple Dec 19 '17 at 15:01
  • See also: https://stackoverflow.com/questions/47136153/what-is-the-fully-qualified-name-of-a-friend-function-defined-inside-of-a-class – M.M May 19 '19 at 09:44

1 Answers1

2
  • Why am I unable to locate str(..) explicitly scoping it with the enclosing namespace?

From the standard, [namespace.memdef]/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 or qualified lookup. [ 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 ]

That means str is not visible to name lookup; it can only be called via ADL.

  • Why does __PRETTY_FUNCTION__ say it's in foo::, and yet I am unable to locate it as such?

From [class.friend]/6,

A function can be defined in a friend declaration of a class if and only if the class is a non-local class ([class.local]), the function name is unqualified, and the function has namespace scope.

str does become member of namespace foo; it's just invisible.

Explanations from cppreference.com:

Names introduced by friend declarations within a non-local class X become members of the innermost enclosing namespace of X, but they do not become visible to lookup (neither unqualified nor qualified) unless a matching declaration is provided at namespace scope, either before or after the class definition. Such name may be found through ADL which considers both namespaces and classes.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405