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?
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 ).
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
}
Related
Suppose we have a class foo from a namespace space which declares a friend function named bar, which is later on defined, like so:
namespace space {
struct foo {
friend void bar(foo);
};
}
namespace space {
void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
To my understanding, friend void bar(foo); declares bar to be a free function inside space taking a foo by value. To use it, we can simply do:
auto f = space::foo();
bar(f);
My understanding is that we don't have to say space::bar, because ADL will see that bar is defined in the same namespace as foo (its argument) and allow us to omit the full name qualification. Nonetheless, we are permitted to qualify it:
auto f = space::foo();
space::bar(f);
which works (and should work) exactly the same.
Things started to get weird when I introduced other files. Suppose that we move the class and the declaration to foo.hpp:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
}
#endif //PROJECT_FOO_HPP
and the definitions to foo.cpp:
#include "foo.hpp"
#include <iostream>
namespace space {
void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
notice that all I did was I moved (didn't change any code) stuff to a .hpp-.cpp pair.
What happened then? Well, assuming that we #include "foo.hpp", we still can do:
auto f = space::foo();
bar(f);
but, we are no longer able to do:
auto f = space::foo();
space::bar(f);
This fails saying that: error: 'bar' is not a member of 'space', which is, well, confusing. I am fairly certain that bar is a member of space, unless I misunderstood something heavily. What's also interesting is the fact that if we additionally declare (again!) bar, but outside of foo, it works. What I mean by that is if we change foo.hpp to this:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
void bar(foo); // the only change!
}
#endif //PROJECT_FOO_HPP
it now works.
Is there something with header / implementation files that alters the expected (at least for me) behaviour? Why is that? Is this a bug? I am using gcc version 10.2.0 (Rev9, Built by MSYS2 project).
There's a slight subtlety that the friend declaration, while it doesn't require a previous declaration of the function or class your class is befriending, does not make the function visible for lookup except via ADL.
cppreference:
A name first declared in a friend declaration within a class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided - see namespaces for details.
This is why you're able to find bar(f) (performs ADL) but not space::bar(f) (fully qualifying the name means ADL is not invoked).
Calling code that doesn't find bar via ADL needs to see a declaration. In the version where everything is in one file, calling code will see the entire definition of space::foo. When you split it into an HPP and a CPP file, calling code only sees the friend declaration which provides limited accessibility.
As you identified, if you want to make the function visible via ordinary lookup, put a declaration of foo in "foo.hpp":
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
void bar(foo); // Now code that includes foo.hpp will see a declaration for bar
}
#endif //PROJECT_FOO_HPP
I have the code:
#include <iostream>
class Foo {
};
namespace Bar {
struct Foo {
};
}
namespace Baz
{
void baz(const Foo &)
{
std::cout << "Hello";
}
}
int main()
{
Baz::baz(Bar::Foo());
}
Compiler can't figure out which Foo want to use and produces the error:
main.cpp: In function 'int main()':
main.cpp:23:19: error: invalid initialization of reference of type 'const Foo&' from expression of type 'Bar::Foo'
23 | Baz::baz(Bar::Foo());
| ^~~~~
main.cpp:15:14: note: in passing argument 1 of 'void Baz::baz(const Foo&)'
15 | void baz(const Foo &)
|
Online compiler
Of course the simplest solution is to use either ::Foo or ::Baz::Foo, but I want to fix all possible ambiguities with O(1) lines of code.
My first idea was using namespace Bar inside Baz namespace:
namespace Baz
{
using namespace Baz;
//...
using-declaration: makes the symbol name from the namespace ns_name accessible for unqualified lookup as if declared in the same class scope, block scope, or namespace as where this using-declaration appears.
I expected that all Bar names become part of Baz namespace, and unqualified lookup prefers Baz::Foo. But for some reason it doesn't work
Online compiler
But using Bar::Foo;, in turn, does the trick. And that confuses me even more
namespace Baz
{
using Bar::Foo;
Online compiler
So, my question is: What is the difference between using namespace Bar and using Bar::Foo in this case?
using namespace Bar::Foo would assume that Bar::Foo is a namespace and import everything from that namespace into current scope, ex.
namespace Bar { namespace Foo { ... all names from this namespace would be imported ... } }
using Bar::Foo would import the class Bar::Foo into scope:
namespace Bar { class Foo {...} }
and if you have nested class Foo within class Bar and want to use it without class specifier, then using would look like:
class Bar {
public:
class Foo {};
};
using Foo = Bar::Foo;
// ...
// ... use Foo instead of Bar::Foo ...
cppreference is your friend.
As regards using namespace ns_name; it reads
using-directive: From the point of view of unqualified name lookup of any name after a using-directive and until the end of the scope in which it appears, every name from ns_name is visible as if it were declared in the nearest enclosing namespace which contains both the using-directive and ns_name.
As regards using ns_name::name; it reads
using-declaration: makes the symbol name from the namespace ns_name accessible for unqualified lookup as if declared in the same class scope, block scope, or namespace as where this using-declaration appears.
It seems complicated, but it isn't. Furthermore, learning things explained this way (which is close to standardese, I'd say) pays off when it comes to diving into the depths of C++.
The situation is that some member function bar::Bar::frobnicate wants to utilize ADL to find a function from some unknown namespace, within a function that has an identical name. However, it only finds its own name.
Testcase
(Note that in reality, Bar is a Foo-agnostic template; this is just the reproducible, minimal testcase)
namespace foo {
struct Foo {};
void frobnicate(Foo const &) {}
}
namespace bar {
struct Bar {
void frobnicate() {
foo::Foo foo;
frobnicate(foo); // <-- error
}
};
}
int main () {
bar::Bar x;
x.frobnicate();
frobnicate(foo::Foo());
}
Results in:
test.cc: In member function ‘void bar::Bar::frobnicate()’:
test.cc:10:31: error: no matching function for call to ‘bar::Bar::frobnicate(foo::Foo&)’
test.cc:10:31: note: candidate is:
test.cc:8:18: note: void bar::Bar::frobnicate()
test.cc:8:18: note: candidate expects 0 arguments, 1 provided
Standard
I understand that this is correct compiler behaviour:
3.4.1 Unqualified name lookup [basic.lookup.unqual]
(...) name lookup ends as soon as a declaration is found for the name (...)
and only after unqualified lookup failed, argument dependent lookup comes into play:
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered
during the usual unqualified lookup (3.4.1) may be searched
Workaround
My current workaround is to introduce a special traits class that does not define the clashing name itself:
struct BarTraits {
void frobnicate_(foo::Foo const &b) {
frobnicate(b);
}
};
or this ligher version:
void frobnicate_(foo::Foo const &c) { frobnicate(c); }
Question
Are there better alternatives than introducing such traits classes?
Explicitly qualifying the call as foo::frobnicate(foo) is not an option here, because (as mentioned) the Bar class is a template upon Foo in reality and should not only work for types in the foo namespace.
As you found out yourself, adding a member function frobnicate to the class interface of Bar (or Bar<T> in the template case), will prevent ADL from finding foo::frobnicate.
The easiest -and in this case idiomatic- way to add frobnicate functionality to a class Bar (or to a class template Bar<T>) is to add a non-member function frobnicate(Bar) (or function template frobnicate(Bar<T>)) to the namespace bar
namespace foo {
struct Foo {};
void frobnicate(Foo const &) {}
}
namespace bar {
template<class T>
struct Bar {
T t;
};
template<class T>
void frobnicate(Bar<T> const& b)
{
frobnicate(b.t);
}
}
int main () {
bar::Bar<foo::Foo> x;
frobnicate(x);
frobnicate(foo::Foo());
}
If you insist on having a member function, you will have to rename it to something like do_frobnicate(). I would not use type traits tricks to get the same behavior as it is an indirect approach, and makes the class interfaces much harder to understand (remember Stroustrup's motto: "represent your ideas directly in code").
you can use this trick
namespace dummy { void your_func(may be some parameteres); }
struct bar {
void member() {
using dummy::your_func; // now your call will find that and ADL will kick in
I just do it with a proxy function
namespace utils
{
int lookup_adl(int arg)
{
return lookup(arg);// ADL search
}
struct Foo
{
int lookup(int arg) { return ::utils::lookup_adl(arg);}
};
}
This doesn't compile,
#include <boost/intrusive_ptr.hpp>
class X
{
public:
void intrusive_ptr_add_ref(X* blah)
{
}
void intrusive_ptr_release(X * blah)
{
}
};
int main()
{
boost::intrusive_ptr<X> ex(new X);
}
But this does :
#include <boost/intrusive_ptr.hpp>
class X
{
public:
friend void intrusive_ptr_add_ref(X* blah)
{
}
friend void intrusive_ptr_release(X * blah)
{
}
};
int main()
{
boost::intrusive_ptr<X> ex(new X);
}
and this :
#include <boost/intrusive_ptr.hpp>
class X
{
public:
};
void intrusive_ptr_add_ref(X* blah)
{
}
void intrusive_ptr_release(X * blah)
{
}
int main()
{
boost::intrusive_ptr<X> ex(new X);
}
I suppose it has something to do with SFINAE (which I haven't as of yet bothered to understand) ? Does the friend qualifier put the defined function as a free function in the enclosed namespace ?
edit
Whoever removed their post, member functions non-friend as add_ref and release (these specific member functions are not mentioned in the documention...) did solve the problem. What happens with the nested definition with the friend qualifier ?
From the documentation of boost::intrusive_ptr:
Every new intrusive_ptr instance increments the reference count by using an unqualified call to the function intrusive_ptr_add_ref, passing it the pointer as an argument. Similarly, when an intrusive_ptr is destroyed, it calls intrusive_ptr_release; this function is responsible for destroying the object when its reference count drops to zero. The user is expected to provide suitable definitions of these two functions. On compilers that support argument-dependent lookup, intrusive_ptr_add_ref and intrusive_ptr_release should be defined in the namespace that corresponds to their parameter; otherwise, the definitions need to go in namespace boost.
This means that intrusive_ptr_add_ref and intrusive_ptr_release should not be member functions, but free functions (friend functions behave as such). Furthermore they are called without qualification, so they should be in the global namespace or somewhere found by ADL.
Edit: To your question about nested definitions with friend qualifier:
friend functions are defined as non memberfunctions, so friend void intrusive_ptr_add_ref(X* blah) will be called as intrusive_ptr_add_ref(my_x_ptr) instead of as my_x_ptr->intrusive_ptr_add_ref().
I am reading "Local Classes" concept in Object-oriented programming with C++ By Balagurusamy (http://highered.mcgraw-hill.com/sites/0070593620/information_center_view0/).
The last line says "Enclosing function cannot access the private members of a local class. However, we can achieve this by declaring the enclosing function as a friend."
Now I am wondering how the highlighted part can be done?
Here is the code I was trying but no luck,
#include<iostream>
using namespace std;
class abc;
int pqr(abc t)
{
class abc
{
int x;
public:
int xyz()
{
return x=4;
}
friend int pqr(abc);
};
t.xyz();
return t.x;
}
int main()
{
abc t;
cout<<"Return "<<pqr(t)<<endl;
}
I know the code looks erroneous, any help would be appreciable.
Your friend statement is fine.
int pqr() {
class abc {
int x;
public:
abc() : x(4) { }
friend int pqr();
};
return abc().x;
}
int main() {
cout << "Return " << pqr() << endl;
}
Edit:
IBM offers this explanation for the issue raised in the comments:
If you declare a friend in a local class, and the friend's name is unqualified, the compiler will look for the name only within the innermost enclosing nonclass scope. [...] You do not have to do so with classes.
void a();
void f() {
class A {
// error: friend declaration 'void a()' in local class without prior decl...
friend void a();
};
}
friend void a(): This statement does not consider function a() declared in namespace scope. Since function a() has not been declared in the scope of f(), the compiler would not allow this statement.
Source: IBM - Friend scope (C++ only)
So, you're out of luck. Balagurusamy's tip only works for MSVC and similar compilers. You could try handing off execution to a static method inside your local class as a work-around:
int pqr() {
class abc {
int x;
public:
abc() : x(4) { }
static int pqr() {
return abc().x;
}
};
return abc::pqr();
}
There seems to be a misunderstand about local classes.
Normally there are here to help you within the function... and should NOT escape the function's scope.
Therefore, it is not possible for a function to take as an argument its own local class, the class simply isn't visible from the outside.
Also note that a variety of compilers do not (unfortunately) support these local classes as template parameters (gcc 3.4 for example), which actually prevents their use as predicates in STL algorithms.
Example of use:
int pqr()
{
class foo
{
friend int pqr();
int x;
foo(): x() {}
};
return foo().x;
}
I must admit though that I don't use this much, given the restricted scope I usually use struct instead of class, which means that I don't have to worry about friending ;)
I have no solution for the friend thing yet (don't even know if it can be done), but read this and this to find out some more about local classes. This will tell you that you cannot use local classes outside the function they are defined in (as #In silico points out in his answer.)
EDIT It doesn't seem possible, as this article explains:
The name of a function first introduced in a friend declaration is in the scope of the first nonclass scope that contains the enclosing class.
In other words, local classes can only befriend a function if it was declared within their enclosing function.
The friend int pqr(abc); declaration is fine. It doesn't work because the abc type has not been defined before you used it as a parameter type in the pqr() function. Define it before the function:
#include<iostream>
// By the way, "using namespace std" can cause ambiguities.
// See http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.5
using namespace std;
// Class defined outside the pqr() function.
class abc
{
int x;
public:
int xyz()
{
return x=4;
}
friend int pqr(abc);
};
// At this point, the compiler knows what abc is.
int pqr(abc t)
{
t.xyz();
return t.x;
}
int main()
{
abc t;
cout<<"Return "<<pqr(t)<<endl;
}
I know you want to use a local class, but what you have set up will not work. Local classes is visible only inside the function it is defined in. If you want to use an instance of abc outside the pqr() function, you have to define the abc class outside the function.
However, if you know that the abc class will be used only within the pqr() function, then a local class can be used. But you do need to fix the friend declaration a little bit in this case.
#include<iostream>
// By the way, "using namespace std" can cause ambiguities.
// See http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.5
using namespace std;
// pqr() function defined at global scope
int pqr()
{
// This class visible only within the pqr() function,
// because it is a local class.
class abc
{
int x;
public:
int xyz()
{
return x=4;
}
// Refer to the pqr() function defined at global scope
friend int ::pqr(); // <-- Note :: operator
} t;
t.xyz();
return t.x;
}
int main()
{
cout<<"Return "<<pqr()<<endl;
}
This compiles without warnings on Visual C++ (version 15.00.30729.01 of the compiler).