Function overload outside class not seen - c++

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:
Where does standard define bahaviour for such overloads?
What are the possible reasons that compiler behaviour is specified that way?
live example

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.

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.

Related

C++ friend operator definition inside class body serves as function declaration?

I'm a newbie reading the C++ Primer book. It says:
A friend declaration only specifies access. It is not a general declaration of the function. If we want users of the class to be able to call a friend function, then we must also declare the function separately from the friend declaration. To make a friend visible to users of the class, we usually declare each friend (outside the class) in the same header as the class itself.
But I just found that this is not the case for friend operator functions defined inside the class body. In the following code, f could not be found but operator+ is found:
struct X
{
friend void f()
{
// friend functions can be defined in the class
// this does NOT serve as a declaration, even though this is already a definition
// to use this function, another declaration is REQUIRED
}
friend X operator+(const X & x1, const X & x2)
{
// this stuff serves as declaration somehow
return {x1.v + x2.v};
}
void foo()
{
f(); // ERROR: no declaration for f
X tmp = X {1} + X {2}; // CORRECT
}
int v;
};
Could someone tell me what makes this difference?
I am using g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, as a reference.
I have searched many questions involving friend declaration and definition on StackOverflow but did not find an answer on this. Sorry if this question is duplicated.
The difference is the operator+ takes X as parameter, then ADL could find the name; f takes nothing, ADL can't help any.
(emphasis mine)
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 ordinary name 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.
Even if we define the function inside the class, we must still provide a declaration outside of the class itself to make that function visible. A declaration must exist even if we only call the friend from members of the friendship granting class. This means in your example, you should forward declare the function f as shown below:
//forward declare f
void f();
struct X
{
//no ADL here since this function f has no parameters of type X. So we also need to declare this outside struct X which we did by providing forward declaration
friend void f()
{
}
void foo()
{
f(); // WORKS NOW because of the forward declaration
X tmp = X {1} + X {2}; //operator+ found using ADL
}
//other members here
};
The output of the program can be seen here.
The reason why the call to operator+ works is because of ADL which stands for argument dependent lookup. In particular, operator+ can be found using ADL since it has parameters of type X. So in this case, you do not need to explicitly declare operator+ outside struct X.

What is the reason to we can not define friend function in local class?

I have a following snippet code of c++. Declared a class inside main() function.
What is the reason to we can not define friend function in local class?
#include<iostream>
int main()
{
class Foo
{
void foo() {} // Ok
friend void Bar(){}; // Error
};
}
There's a practical reason. First and foremost, an inline friend definition cannot be found by either qualified or unqualified lookup. It can only by found by ADL. So if we take the class from your example, put it in global scope and try to call Bar:
class Foo
{
friend void Bar(){};
void foo() {
Bar();
}
};
We'll be notified that Bar was not declared in that scope. So if it was in a local class. You can't call it from the members. You can't call it inside the function. The only way you may call it involves either some hoops or ADL. So the language just doesn't allow for it. It's not deemed a useful feature.
There is no convincing technical reason for this. ADL can't find it, you say? Well, that's basically part of the question: why can't ADL find it? Just extend ADL to make it find it.
One design-level reason for this limitation is probably rooted in the language treatment of unqualified names in friend declarations.
Unqualified names used in friend declarations in local classes refer to names from nearest enclosing non-class scope. This treatment of unqualified names is critically important, since this is the only way local classes can refer to each other (since for obvious reasons, local classes do not have qualified names)
int main()
{
class B;
class A {
int x;
friend B; // refers to local `B`
};
class B {
void foo(A &a) { a.x = 42; }
};
}
This rule is also applied to friend function declarations with unqualified names. C++ does not have local functions, but such friend declarations can still refer to a local non-defining declaration of a function (which is perfectly legal)
void foo() {}
int main()
{
void foo(); // refers to `::foo`
class A {
friend void foo(); // refers to local `foo`, which is `::foo`
};
}
Now, what would you propose should happen when one defines a friend function in a local class (using an unqualified name, of course)? What function is introduced by such a declaration? Into what scope, specifically? By definition, it is not a member of the class, since friend declarations do not introduce class members. It cannot be a member of the nearest enclosing local scope, since that would make it a local function and C++ does not support local functions.
We cannot just uniformly change the behavior of all unqualified names in friend declarations and say that they should now refer to names in the nearest enclosing namespace scope, since that would leave as with no way to refer to local classes (as shown above).
The only way out of this situation is to make only in-class friend function definitions refer to (and define) functions in the nearest enclosing namespace scope. Such functions would only be callable through [modified] ADL (and let's say we are OK with that). But that would mean that we'd have to give different treatment to unqualified names in friend function definitions (as opposed to non-defining friend declarations). This would be rather inelegant and confusing. So, the language authors decided against it.
Note that importance of this might have increased notably after C++14, which gave us deduced auto return types in functions. After that local classes became not even nearly as "local" as they used to be
auto foo()
{
struct S // Local class
{
void bar() {}
};
return S();
}
int main()
{
auto a = foo();
a.bar(); // An object of local class used outside of its original scope
typedef decltype(a) S; // Local type is "stolen" from its original scope
S b; // and used to freely declare objects in a completely
b.bar(); // different scope
}
Because member functions of a local class have to be defined entirely inside the class body and friend function not a member function. We declared friend functions inside class and defined outside of class.
According to cppreference:
Local classes
A local class cannot have static members
Member functions of a local class have no linkage
Member functions of a local class have to be defined entirely inside the class body
Local classes other than closure types (since C++14) cannot have member templates
Local classes cannot have friend templates
Local classes cannot define friend functions inside the class definition
A local class inside a function (including member function) can access the same names that the enclosing function can access.

Which look up rule prevents the compiler from finding the function?

void Foo(int i)
{
}
struct Bar
{
static void Foo() { Foo(1); }
};
The above code does not compile. It cannot find Foo(int). Why? I know it has to do with having the same function name, but did not get further understanding the problem.
Why do I need a qualified lookup?
From 3.4.1 (emphasis mine)
In all the cases listed in 3.4.1, 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. If no declaration is
found, the program is ill-formed.
In this particular case the first scope to be searched is the function local scope. Foo is not found there. Then comes the class scope. Since the name Foo is found in it, the name lookup stops there and the other Foo does not participate in overload resolution at all.
If a function in a particular namespace shares the same name as one in a global namespace, then the one in the global namespace is not found.
Unfortunately the C++ standard does not allow you to bring in the global namespace into the current one with an appropriate using statement, although you can use using ::Foo; to pull in that particular function:
void Foo(char)
{
}
struct Bar
{
static void Foo(double) { }
static void Foo() {
using ::Foo;
Foo('c'); // correct overload is found since ::Foo is pulled into scope
Foo(3.0);
}
};
Otherwise you need to use the scope resolution operator to find the global function:
struct Bar
{
static void Foo() { ::Foo(1); }
};

Is it possible to bring global function into the overload resolution with member function?

Here is the corresponding question, what I want to know is, Is it possible to bring global function into the overload resolution with member function?
I tried in 2 ways, but both don't work:
void foo(double val) { cout << "double\n";}
class obj {
public:
using ::foo; // (1) compile error: using-declaration for non-member at class scope
void callFoo() {
using ::foo; // (2)will cause the global version always be called
foo(6.4);
foo(0);
}
private:
void foo(int val) {cout << "class member foo\n"; }
};
I doubt you can make the compiler call one or the other based on the type. You can of course use a local wrapper function, something like this:
void callFoo() {
foo(6.4);
foo(0);
}
private:
void foo(double val) { ::foo(val); }
The wrapper function should nicely inline into nothing, so there is no actual overhead when compiled with optimisation.
Or don't call the member and the global function the same name, which makes life a whole lot easier!
This is plain old unqualified name lookup, specified in §3.4.1 [basic.lookup.unqual]:
1 In all the cases listed in 3.4.1, 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. If no
declaration is found, the program is ill-formed.
8 For the members of a class X, a name used in a member function
body, in a default argument, in an exception-specification, in the
brace-or-equal-initializer of a non-static data member (9.2), or in the definition of a class member outside of the definition of X,
following the member’s declarator-id, shall be declared in one of
the following ways:
before its use in the block in which it is used or in an enclosing block (6.3), or
shall be a member of class X or be a member of a base class of X (10.2), or
if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies
in turn to Y’s enclosing classes, starting with the innermost
enclosing class), or
if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition
of class X, or
if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within
a local class of a function that is a member of N, before the use of
the name, in namespace N or in one of N’s enclosing namespaces.
Note first that name lookup stops as soon as a declaration is found. So if you have using ::foo; in callFoo(), lookup for foo will end there and never touch the second bullet point; if you don't have it, lookup for foo will find the member foo() at the second bullet point and never search elsewhere. The way unqualified name lookup is specified means that you will either find class members or non-class-members, but never both.
This is also noted in §13.1.1.1 [over.call.func]/p3:
In unqualified function calls, the name is not qualified by an -> or .
operator and has the more general form of a primary-expression. The
name is looked up in the context of the function call following the
normal rules for name lookup in function calls (3.4). The function
declarations found by that lookup constitute the set of candidate
functions. Because of the rules for name lookup, the set of candidate
functions consists (1) entirely of non-member functions or (2)
entirely of member functions of some class T. In case (1), the argument list
is the same as the expression-list in the call. In case (2), the
argument list is the expression-list in the call augmented by the
addition of an implied object argument as in a qualified function
call.
A using-declaration at class scope must name a base class member (§7.3.3 [namespace.udecl]/p3):
In a using-declaration used as a member-declaration, the
nested-name-specifier shall name a base class of the class being defined.
What it appears you are trying to do is use the parameter type to choose the correct overload, and to bring the external foo into your class or member function.
Bringing it into the class is easy. You just declare another overload as a class member that forwards to the external one.
Bringing into the function is tricky but can be done by use of helper functions e.g. fooCaller( double ) and fooCaller( int ) which you can call from callFoo.
If you don't want to expose these to the external user, i.e. hide them away in the cpp file, it can be done but is trickier, due to the fact that foo in your class is private. You would have to have your callFoo function pass in the private function as a member function to "call" for the int overload.
You cannot do that probably because member functions has different signature visible by compiler, which is
void foo(double)
vs
void obj::foo(int)

Defining namespaced member vs free functions without qualification

I sometimes declare classes in nested namespaces and when it comes to defining their member functions, I prefer not to have to qualify each one with these nested namespace names, especially if they are long-ish.
Adding "using namespace " (or, for more precise targetting, "using ::SomeClass") before I define the member functions seems to obviate the need to qualify each definition, but I can't find anywhere in the spec that guarantees this, and I'm worried that it might be a behaviour that only works with GCC. I note that there doesn't appear to be a similar mechanism for skipping the need to add the qualifiers when defining free functions(?).
As an example of what I mean:
Header:
// example.h
namespace SomeNamespace
{
class SomeClass
{
public:
void someMemberFunction();
};
void someFreeFunction();
};
Implementation:
// example.cpp
#include "example.h"
using namespace SomeNamespace;
void SomeClass::someMemberFunction()
{
// OK: seems to define SomeNamespace::SomeClass::someMemberFunction(),
// even though we didn't qualify it with SomeNamespace::
}
void someFreeFunction()
{
// Not what we wanted; declares and defines ::someFreeFunction(), not
// SomeNamespace::someFreeFunction() (quite understandably)
}
int main()
{
SomeClass a;
a.someMemberFunction(); // Ok; it is defined above.
SomeNamespace::someFreeFunction(); // Undefined!
return 0;
}
So my question: is the above way of definining SomeClass::someMemberFunction() legal, and where in the spec is this mentioned? If legal, is it advisable? It certainly cuts down on clutter! :)
Many thanks :)
Perhaps I'm getting this wrong but if you have:
// example.h
namespace SomeNamespace
{
class SomeClass
{
public:
void someMemberFunction();
};
void someFreeFunction();
};
You can also simply write:
#include "example.h"
// example.cpp
namespace SomeNamespace
{
void SomeClass::someMemberFunction()
{
}
void someFreeFunction()
{
}
}
When you define a member-function, the compiler realizes that it is a member-function that must belong to a previously declared class, so it looks that class up, as specified in Section 9.3.5 of the standard:
If the definition of a member function is lexically outside its class
definition, the member function name shall be qualified by its class
name using the :: operator. [Note: a name used in a member function
definition (that is, in the parameter-declaration-clause including
the default arguments (8.3.6), or in the member function body, or, for
a constructor function (12.1), in a mem-initializer expression
(12.6.2)) is looked up as described in 3.4. ] [Example:
struct X {
typedef int T;
static T count;
void f(T);
};
void X::f(T t = count) { }
The member function f of class X is defined in global scope; the
notation X::f specifies that the function f is a member of class X and
in the scope of class X. In the function definition, the parameter
type T refers to the typedef member T declared in class X and the
default argument count refers to the static data member count
declared in class X. ]
Basically, what you are doing is fine. However, there is another (preferable) way to cut down on the clutter when using nested namespaces, or namespaces with long names (or both) - define an alias:
namespace short_name = averylong::nested::namespacename;