1

Consider following code snippet:

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void foo() {
        foo(Bar::A);
    }
};

It fails to compile, the message from gcc 9.2 is:

:12:19: error: no matching function for call to 'Baz::foo(Bar)'
 12 |         foo(Bar::A);
    |              

I don't suspect it is a bug since clang 10 also fails. I have two questions regarding this situation:

  1. Where does standard define bahaviour for such overloads?

  2. What are the possible reasons that compiler behaviour is specified that way?

live example

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
bartop
  • 9,971
  • 1
  • 23
  • 54
  • foo(Bar) is in a different namespace then foo(). Every call in Baz to foo, will direct to foo() – RoQuOTriX Apr 22 '20 at 14:34
  • @RoQuOTriX Don't answer in comments – Asteroids With Wings Apr 22 '20 at 14:36
  • 2
    I'll let a real language lawyer give the proper answer, but the short version is: the two `foo`s are not overloads, they form two different overload sets, each containing only a single option. When resolving the name `foo`, the compiler will start searching for the name in the tightest scope (inside the function), fail, then look in the next scope (class), find an overload set containing one function, and try to resolve the call within that set, and, obviously, fail. – DeducibleSteak Apr 22 '20 at 14:36
  • 1
    @AsteroidsWithWings my comment doesn't answer his questions. For you to remember, the questions are: 1. "Where does standard define bahaviour for such overloads?" 2 "What are the possible reasons that compiler behaviour is specified that way?" – RoQuOTriX Apr 22 '20 at 14:38
  • 1
    @RoQuOTriX I agree it's not a good answer. But it is a technical response to the question. You put it where it cannot be peer reviewed. Please do not do that. Comments are for requesting clarification. Hover your mouse over the "add a comment" link and you'll see. Thanks. – Asteroids With Wings Apr 22 '20 at 14:40
  • 1
    Issue with question with several questions is that we might have incomplete answers. – Jarod42 Apr 22 '20 at 14:41
  • @AsteroidsWithWings OP could have included that information in the question, and the question would still remain the same, what in the standard makes it so? – 463035818_is_not_an_ai Apr 22 '20 at 14:42
  • 1
    Rational is to not break `struct S { void foo(int); void bar() { foo('*'); }};` with previous `#include` which might declare `void foo(char);`. – Jarod42 Apr 22 '20 at 14:47
  • Related https://stackoverflow.com/questions/25863485/is-it-possible-to-bring-global-function-into-the-overload-resolution-with-member – anastaciu Apr 22 '20 at 14:49

2 Answers2

6

The call to foo inside Baz::foo() will only look up names inside the class. If you mean to use the foo declared outside the class Baz, you need to use the scope-resolution operator, like this:

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void foo() {
        ::foo(Bar::A);   // looks up global 'foo'
    }
};

Note that the unscoped call to foo fails because there is a Bar::foo that is found in the closest scope. If you name the function differently, then no function is found in Bar, and the compiler will look in the outer scope for the function.

enum class Bar {
    A
};

void foo(Bar) {}

struct Baz {
    void goo() {   // not 'foo'
        foo(Bar::A);  // this is fine, since there is no 'Bar::foo' to find
    }
};

Here's the quote from cppreference for a class definition.

e) if this class is a member of a namespace, or is nested in a class that is a member of a namespace, or is a local class in a function that is a member of a namespace, the scope of the namespace is searched until the definition of the class, enclosing class, or function. if the lookup of for a name introduced by a friend declaration: in this case only the innermost enclosing namespace is considered, otherwise lookup continues to enclosing namespaces until the global scope as usual.

Of course, this only applies to class definitions, but for member functions (which is your example), it says

For a name used inside a member function body, a default argument of a member function, exception specification of a member function, or a default member initializer, the scopes searched are the same as in [class definition], ...

So the same logic applies.

cigien
  • 57,834
  • 11
  • 73
  • 112
5

According to the rule of unqualified name lookup, from the standard, [basic.lookup.unqual]/1,

(emphasis mine)

In all the cases listed in [basic.lookup.unqual], the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name.

That means the name foo is found at the class scope (i.e. the Baz::foo itself), then name lookup stops; the global one won't be found and considered for the overload resolution which happens later.

About your 2nd question, functions can't be overloaded through different scopes; which might cause unnecessary confusion and complexity. Consider the following code:

struct Baz {
    void foo(int i) { }
    void foo() {
        foo('A');
    }
};

You know 'A' would be converted to int then passed to foo(int), that's fine. If functions are allowed to be overloaded through scopes, if someday a foo(char) is added in global scope by someone or library, behavior of the code would change, that's quite confusing especially when you don't know about the adding of the global one.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405