Doesn't ADL looks up static member functions? - c++

This is follow up question from Does argument dependent lookup only search namespaces or classes too? , In which #David Rodríguez said "ADL will look in the enclosing namespace of the type, and also inside the type itself" . I may have got him wrong what he tried to say but I was trying this example:
struct foo{
static void bar(foo* z){}
};
int main(){
foo* z;
bar(z);
}
It doesn't compiles, producing the error " ‘bar’ was not declared in this scope " . Is it the case that ADL doesn't considers the static member function?. I mean in the example associated class is foo so wouldn't ADL look inside the class? . Can anyone please simplify the rules here?

He probably meant this:
struct foo{
friend void bar(foo* z){} //not static, its friend now
};
foo* z;
bar(z); //fine now
But then technically bar() is not inside foo. It is still in the enclosing namespace of foo.
--
EDIT:
He indeed meant friend, as he said (emphasis mine):
The best example is a friend function that is defined inside the type
And his example illustrates further. Probably you need to read "defined inside", rather than only "inside".
The word "defined" is all that makes the difference, because it looks like the function's name bar is introduced into the scope of the class, but in actuality, the name bar is introduced into the enclosing namespace of foo (see §3.3.1/3-4 and §11.3/6).
Here is a better example:
namespace Demo
{
struct foo
{
friend void bar(foo* z){}
};
}
foo *z;
bar(z); //foo (type of z) is inside Demo, so is bar
//(even though bar is defined inside foo!)
bar(NULL); //error - NULL doesn't help ADL.
bar(nullptr); //error - nullptr doesn't help ADL.
bar(static<foo*>(NULL)); //ok - ADL
Note that the name bar, even though is introduced into the namespace Demo, is hidden, and thus cannot be used from outside using usual name-lookup:
using namespace Demo; //brings ALL (visible) names from Demo to current scope
bar(NULL); //STILL error - means bar is invisible
Or,
Demo::bar(NULL); //error - not found
Demo::foo::bar(NULL); //error - not found
Hope that helps.

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.

Using declaration and multiple inheritance

We know that a 'using declaration' for a namespace's member name in a scope where another entity is defined there with the same name, causes a compile time error: "symbol x is already defined".
The error is detected at the point the 'using declaration' appears not at use point like a 'using directive'.
A using declaration in a class scope allows only exposing a base class member name.
Here is an example:
#include <iostream>
using namespace std;
struct Foo{
Foo(int x) : x_(x){}
int x_ = 0;
};
struct Bar{
Bar(int y) : y_(y){}
int y_ = 0;
};
struct FooBar : Foo, Bar{
using Foo::Foo; // inheriting Foo's ctors
using Bar::Bar; // inheriting Bar's ctors
int x_ = 10;
// using Foo::x_; // error detected here
};
int main(){
FooBar fb(5); // error detected here
}
As you can see above the using declaration for Foo::x_ causes a compile-time error at the point the using appears and it is OK.
But the using declaration for base classes Foo and Bar constructors don't issue an error until the usage in main?!!
Normally FooBar inherits Foo(int); so when using Bar::Bar; appears normally the compiler complains but the code works fine until I try to use that constructor. FooBar(int).
After inheriting all the constructors, the compiler defines a derived class ctor and make it call its base class one passing in its parameters to it so it is as if we wrote:
struct FooBar : Foo, Bar{
FooBar(int x) : Foo(x){}
FooBar(int x) : Bar(x){}
//...
};
So why the compiler allows such bug? and wait until the use of that ctor to flag ambiguity??
It looks to me that a using declaration in such case undergoes the same thing like a using directive.
It is a general (though not universal) principle of C++ that a prefix of a program is valid if and only if there is some valid completion of it that uses the declarations in the prefix. This is why it’s not an error to declare an unused variable: it could be used, and it’s “too late” to reject it later if it’s not. Here, that principle is generalized a bit to say that, because overload resolution can usually distinguish between functions from different namespaces or base classes, there is no check for conflict at all.
Not so for variables, since you can’t possibly use the ambiguous combination. Also not so if one of the conflicting functions declarations is a direct member and a sibling of the using-declaration: in that case, you can’t possibly use the function you declared, so you are presumed not to have meant it.

Function overload outside class not seen

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.

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); }
};

Calling a function overloaded in several namespaces from inside one namespace

I have the following code snippet:
void foo(double a) {}
namespace bar_space
{
struct Bar {};
void foo(Bar a) {}
}
foo(double) is a general function from a library.
I have my own namespace bar_space with my own struct, Bar. I would like to implement an overloading of foo() for Bar, thus making Bar more similar to the built-in types.
Trouble appears when I attempt to call the original foo(double) from within the namespace:
namespace bar_space
{
void baz()
{
foo(5.0); // error: conversion from ‘double’ to non-scalar type ‘ssc::bar_space::Bar’ requested
}
}
This fails to compile on gcc on both my Fedora and Mac.
Calling
foo(5.0)
from outside the namespace or using
namespace bar_space
{
::foo(5.0)
}
works ok, but this doesnt make my new function quite as nice as I had hoped for (other developers are also working inside bar_space).
Is bar_space hiding the original function? Is there a way to make foo(5.0) callable from within bar_space without explicit scoping (::)? Any help is appreciated.
In C++, there is a concept called name hiding. Basically, a function or class name is "hidden" if there is a function/class of the same name in a nested scope. This prevents the compiler from "seeing" the hidden name.
Section 3.3.7 of the C++ standard reads:
A name can be hidden by an explicit
declaration of that same name in a
nested declarative region or derived
class (10.2)
So, to answer your question: in your example void foo(double a); is hidden by void bar_space::foo(Bar a); So you need to use the :: scoping operator to invoke the outer function.
However, in your sample code you could use something like that
namespace bar_space
{
using ::foo;
void baz()
{
Bar bar;
foo(5.0);
foo(bar);
}
}
Yes, bar_space is hiding the original function and no, you can't make foo(5.0) callable from whithin bar_space without explicit scoping if foo(double) is defined in the global namespace.