Why class member functions shadow free functions with same name? - c++

It recently came to my attention that member functions completely shadow free functions with the same name when inside the class. And by completely I mean that every free function with the same name is not considered for overload resolution at all. I can understand why it's done with something like this:
void f();
struct S
{
void f();
void g()
{
f(); // calls S::f instead of ::f
}
};
where the functions have identical signatures, its only natural as variable scoping works the same way. But why prohibit unambigious calls where free function has different signature like this:
void f();
struct S
{
void f(int x);
void g()
{
f(); // fails to compile attempting to call S::f, which has wrong signature
}
};
I am not asking how to call a shadowed free function from inside the class. What i want to know is the rationale behind this design.

For unqualified name lookup, only one scope at a time is considered, and if the search in that scope doesn't yield any results, the next higher scope is searched. In your case, only S's scope is searched.
But why prohibit unambigious calls where free function has different signature like this:
The problem is that name lookup doesn't concern itself with anything but the name, the identifier. It is completely oblivious to the fact that you want to call a function, it just sees an identifier. The same name lookup happens if you just use auto x = f;, and if you think of it that way, there are very good reasons you only want a very limited scope to search. Anything else would just surprise the user.

There is a special, very surprising, rule (but it does not apply to your example) stating that once a class member name is found by name lookup, no namespace scopes are searched:
#include <string>
struct C {
std::string s;
explicit C (std::string);
void swap (C& rhs) {
swap (s, rhs.s); // error: swap is C::swap
}
};
void swap (C& lhs, C& rhs) {
swap (lhs.s, rhs.s); // std::swap(string,string)
}
IMO, this is craziness.
But why prohibit unambigious calls where free function has different signature like this:
Name lookup happens before overloading resolution:
If lookup is ambiguous, overloading resolution is not done.
If no viable function is found by name lookup, no other round of lookup is tried.
The rules are sufficiently complex without "feedback" between overloading and name lookup. I would suggest simplification (like removing the member hides namespace scope name rule, and removing ambiguous name lookup) rather than complexification.

I cannot provide an authoritative answer (Maybe some remember a quote from Design and Evolution of C++ or actually has been on the committee at that time), but my first guess would be to exactly fail in cases as you show. It is easy to forget how many things are in scope at a certain time. Additionally overload resolution can be quite complex and there can be default arguments and conversion. So I'd rather have the most limited scope in that case to be always sure what exactly is being called.

Related

Why do member names hide type-dependent name lookup? [duplicate]

void foo(int)
{
}
class X
{
void foo()
{
}
void bar()
{
foo(42);
// error: no matching function for call to 'X::foo(int)'
// note: candidate is:
// note: void X::foo()
// note: candidate expects 0 arguments, 1 provided
}
};
Why is C++ unable to call the free function (which is the only one with the correct signature)?
Because the two identifiers are defined in different scopes, and overload resolution only concerns about functions in the same scope. Once the compiler finds that the class has a foo, it stops climbing up to wider scopes (C++11 §3.4.1/1), so the free function foo is hidden.
You need to use a qualified name to refer to the global foo:
::foo(42);
The logical reason is Consistency.
Suppose as per the suggestion, compiler resolves foo(42) to
::foo(int).
Now after sometime, if you change X::foo() to X::foo(int) then
foo(42) will be resolved to X::foo(int). Which is not consistent.
That is the also the reason why derived class function hides base class function when there are similar names.
Such cases can be resolved in 2 ways;
(1) Give fully qualified name (e.g. ::foo(42))
(2) Use using utility; e.g.
void bar()
{
using ::foo;
foo(42);
}
A name in an inner scope hides names in outer scopes. It doesn't matter if it is a function or something else, or if you are in a class or a namespace.
Only if the name lookup finds several functions with the same name will the overload resolution kick in to try to select the one that is the best match for the call.
Really like your question. Also I could say, use this syntax:
::foo(42);
But I can say that in my opinion it's more elegant and good programming, set namespaces, so you can write something like this:
namespace MyNameSpace
{
void foo(int){}
class X
{
void foo(){}
void bar()
{
MyNameSpace::foo(42);
}
};
};
This is a good thing because Namespaces allow to group classes, objects and functions under a name.
PS: Then this help you to understand the meaning of write ::foo(42); when you haven't any namespace.
I cannot answer the why part of your question -- I do not know what was the rationale behind that in the language spec.
To call the global function in your example, use the :: syntax:
::foo(42);
The reason for this is the fact, that the compiler will look for a matching function name first, ignoring return values and parameters. When inside a class, it will try to look for a matching member there (in fact, it will look through all scopes going "upwards"; local scope(s), function scope, class scope, namespace scope, global scope, etc.).
X::foo is the first matching name. THEN (not before) it will try to pick the right overload (if there are multiple declarations) based on the parameters (which is the reason you can overload the same function with different parameters but not different return values only) and then it will check the return value (if there's any).

Why can't I overload a non-class function with a class function?

I have two overloaded functions 'func'. func(int,int) is defined outside class and func(int) is defined inside. How can I call func(int,int) from inside the class's member functions?
#include <iostream>
using namespace std;
int func(int a, int b)
{ return a+b;}
class test
{
int a;
public:
int func(int);
int driver();
};
int test::func(int b)
{ return b;}
int test::driver()
{ return func(10,20);}
int main()
{
test A;
cout<<A.driver(); //ERROR: NO MATCHING FUNCTION TO CALL FUNC(INT,INT)
return 0;
}
return ::func(10,20);
Using :: in front of a name means to look in the global namespace. It will then find the global func, not the func contained in the class.
EDIT:
func in the class isn't overloading the global func per se, but is hiding it. When lookup starts for func when it is called, it finds test::func first, even though ::func is a better match. Changing the name or removing test::func will remove the first match that lookup came across, and it will proceed until it finds the next one, ::func.
As to why it doesn't start in the global namespace, there are many reasons. I can't speak for the people who designed it, but these are all problems that would occur if it were changed.
1: Anybody writing code in a namespace would have to qualify every name with the namespace (std::cout vs cout). Failure to do so could lead to silent bugs where the wrong variables were changed, depending on what existed in the scopes above it.
2: Unqualified lookup would find functions less likely to be related to the code sooner (the function right next to where you called it is far more likely to be related to your code than the one in a different header file two namespaces above you).
3: Functions would have the opposite lookup of variables. I can't specify the scope of a local inside a function (void func() { int i; /*no*/func::i = 0;/*no*/}), so lookup for variables would still have to start in the enclosing scope and work outwards. It would make no sense for the two to work in opposite directions.

gcc template resolution and namespaces

Warning, C++ hating ahead...
I have seen here slightly different variations of this issue, here is my take
namespace space {
}
template <typename T> struct C
{
void foo() {
using namespace space;
bar(t);
}
T t;
};
class A {
};
namespace space {
void bar(const A& a){
}
}
int main()
{
C<A> c;
c.foo();
return 0;
}
if bar is not inside a namespace, everything compiles ok. Putting a namespace breaks compilation with gcc. What is more interesting - gcc finds the function, but just don't feel like using it:
ConsoleApplication1.cpp: In instantiation of 'void C<T>::foo() [with T = A]':
ConsoleApplication1.cpp:26:10: required from here
ConsoleApplication1.cpp:8:8: error: 'bar' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point
instantiation [-fpermissive]
bar(t);
^
ConsoleApplication1.cpp:18:6: note: 'void space::bar(const A&)' declared here, later in the translation unit
void bar(const A& a){
Is there any sane reason for this behaviour, except that the standard says so? And is there any switch I can make gcc accept this code, since it seems there is no technical issue, and I do not see why such code should not work from a pure language standpoint.
templates are not macros. Name lookup of symbols is done in two contexts: first, where you wrote the template, and second pass of only argument-dependent lookup (ADL, or Koeing Lookup) when you pass the template a type (when the template "factory" is instantiated into an actual class or function).
When the function is in the same namespace as A, it is found via ADL. When it is not, it will not be. So when you moved the bar function into space::bar, it was no longer ADL-found with an argument of type A.
This is intentional, to both prevent your template from meaning completely different things at different spots, and to allow non-member interface extensions to types in their own namespace only.
Either use a traits class, stick such helper functions in the same namespace as the type, or pass in functors.
A traits class is probably easiest. Create a class with a static function foo (if you want a default implementation: otherwise leave empty). Call it from your template. Specialize it for A. Now you can implement special behavior for your A type at that point -- it could even call your space::bar function if that function is in view.
Putting bar in the same namespace as A is another solution, and I find it scales better than traits classes. Many of my personal traits classes end up falling back on an ADL lookup, as that lets me inject the handling code right next to where I define A. (The use of traits classes lets me also have my trait handle things in std, where I am not allowed to inject functions for ADL purposes for types living in std (you can inject ADL functions into std, but only for your own types))
The functor solution is how std::map works -- while it falls back on std::less<T> which falls back on operator<, it takes a functor that lets you specify how the key type should be compared within this map.
Is there any sane reason for this behaviour, except that the standard says so?
Sane or not, C++ usually requires names to be declared before use. You don't declare bar before using it; specifically, using namespace space only imports names that have been declared at that point, and not bar.
(If you care about the reason, it's because C++ inherited its declaration rules from the languages of half a century ago, when computers were somewhat primitive. Rules like this allowed compilation in a single pass, so that the compiler didn't have to keep waiting for the operator to put the stack of punch cards back into the hopper. Or something like that; I'm not quite sure how computers worked back then.)
And is there any switch I can make gcc accept this code
As the error message says, -fpermissive. But your code won't be portable if you don't stick to the standard.
This error has nothing to do with namespaces or templates at all, but is the standard error of using a function before it has been declared. Just as you cannot do:
void caller() { callee(); }
void callee() {}
But must instead do:
void callee() {}
void caller() { callee(); }
Or:
void callee();
void caller() { callee(); }
void callee() {}
... the same applies for more complicated functions involving templates and namespaces. Note that if you reorder slightly, it works just fine:
class A {
};
namespace space {
void bar(const A& a){
}
}
template <typename T> struct C
{
void foo() {
using namespace space;
bar(t);
}
T t;
};
int main()
{
C<A> c;
c.foo();
return 0;
}

member function hiding free function

void foo(int)
{
}
class X
{
void foo()
{
}
void bar()
{
foo(42);
// error: no matching function for call to 'X::foo(int)'
// note: candidate is:
// note: void X::foo()
// note: candidate expects 0 arguments, 1 provided
}
};
Why is C++ unable to call the free function (which is the only one with the correct signature)?
Because the two identifiers are defined in different scopes, and overload resolution only concerns about functions in the same scope. Once the compiler finds that the class has a foo, it stops climbing up to wider scopes (C++11 §3.4.1/1), so the free function foo is hidden.
You need to use a qualified name to refer to the global foo:
::foo(42);
The logical reason is Consistency.
Suppose as per the suggestion, compiler resolves foo(42) to
::foo(int).
Now after sometime, if you change X::foo() to X::foo(int) then
foo(42) will be resolved to X::foo(int). Which is not consistent.
That is the also the reason why derived class function hides base class function when there are similar names.
Such cases can be resolved in 2 ways;
(1) Give fully qualified name (e.g. ::foo(42))
(2) Use using utility; e.g.
void bar()
{
using ::foo;
foo(42);
}
A name in an inner scope hides names in outer scopes. It doesn't matter if it is a function or something else, or if you are in a class or a namespace.
Only if the name lookup finds several functions with the same name will the overload resolution kick in to try to select the one that is the best match for the call.
Really like your question. Also I could say, use this syntax:
::foo(42);
But I can say that in my opinion it's more elegant and good programming, set namespaces, so you can write something like this:
namespace MyNameSpace
{
void foo(int){}
class X
{
void foo(){}
void bar()
{
MyNameSpace::foo(42);
}
};
};
This is a good thing because Namespaces allow to group classes, objects and functions under a name.
PS: Then this help you to understand the meaning of write ::foo(42); when you haven't any namespace.
I cannot answer the why part of your question -- I do not know what was the rationale behind that in the language spec.
To call the global function in your example, use the :: syntax:
::foo(42);
The reason for this is the fact, that the compiler will look for a matching function name first, ignoring return values and parameters. When inside a class, it will try to look for a matching member there (in fact, it will look through all scopes going "upwards"; local scope(s), function scope, class scope, namespace scope, global scope, etc.).
X::foo is the first matching name. THEN (not before) it will try to pick the right overload (if there are multiple declarations) based on the parameters (which is the reason you can overload the same function with different parameters but not different return values only) and then it will check the return value (if there's any).

forward declaration and template function error

Currently I have a frustrating problem with forward declaration and template function. I have been trying to googling and do some modification but nothing has worked so far. Below is the snippet of the code:
class TaskScheduler; --> //forward declaration of ‘struct TaskScheduler’
//
//
class TaskEvent {
//
//
};
class HostTask {
//
//
};
template<class T> inline HostTask*
findT(TaskScheduler* tss, T* e)
{
map<int, HostTask*>::iterator it;
bool bEq = false;
for(it = tss->tasks_.begin(); it != tss->tasks_.end(); it++) { --> //error: invalid use of incomplete type ‘struct TaskScheduler’
if(dynamic_cast<TaskEvent*>(e))
bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
else if(dynamic_cast<HostTask*>(e))
bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
if(bEq) {
return it->second;
}
}
return NULL;
}
//
//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
friend HostTask* findT<TaskEvent>(TaskScheduler* tss, TaskEvent* e); //findT function is used here
//
//
};
Here is the error message that I've got which is shown in the code as well:
./bt-taskscheduler.h:159: error: forward declaration of ‘struct TaskScheduler’
./bt-taskscheduler.h:229: error: invalid use of incomplete type ‘struct TaskScheduler’
Could anybody show me what is going wrong in this code? Any help is appreciated..
In the definition of findT you are using tss->tasks_ which dereferences a pointer to an object of type TaskScheduler so you need a full definition of the struct, not just a forward declaration visible at this point in the program.
The definition of struct TaskScheduler needs to appear before the definition of the findT function template.
You are using the TaskScheduler class in your for-loop header "tss->tasks_.begin()". Compiler does not know, whether this class have "tasks_" member or not.
It is not the problem with your templates, any function, inlined in the header file will cause the same error. Forward declaration of the class only allows you to declare pointers (or references) to that class or pass this class objects as a parameters. You cannot "use" the class (call its methods or get the member data), until you fully define your class.
Because you use the definition of TaskScheduler in the findT functions, you have two options:
Move the definition of TaskScheduler above the findT template function
Make TaskScheduler a second template of of the findT function
Like this:
template< class U, class T>
inline HostTask* findT( U* tss, T* e)
{
//...
}
Next to trouble with the forward declaration, it looks as if your findT function should actually be a member function of the scheduler class: it makes extensive use of the scheduler's data members.
These members are private, so you need a way to publish them, and fall back onto the friend declaration.
So either you make the members public, or, better, you refactor the findT function into a member function.
There's no problem in making it a templated member function, either. And you will automatically get rid of the friend declaration.
//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
public:
template<class T> inline HostTask* findT(T* e) const
{
map<int, HostTask*>::iterator it;
bool bEq = false;
for(it = tasks_.begin(); it != tasks_.end(); it++) {
if(dynamic_cast<TaskEvent*>(e))
bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
else if(dynamic_cast<HostTask*>(e))
bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
if(bEq) {
return it->second;
}
}
return NULL;
}
};
As other posters have mentioned, you are dereferencing a pointer to TaskScheduler without a definition of the type, which will cause an error just as it would in any definition.
What you are probably confused about is that your code likely works on some compilers, even modern ones (I know MSVC is incorrect in this regard, but I do not know if it will accept the above code*). These compilers do not properly implement what is known as two-phase name lookup.
Two-phase name loopkup is a more predictable method of name lookup used in templates than the simpler form used by some compilers. In the simpler form, the template definition is parsed and stored for use only when it's instantiated, and name lookup is performed on all names in the template from the point at which you instantiate the template. With two-phase name lookup, names used within a template are sorted into dependent names and non-dependent names. Non-dependent names are names that the compiler can resolve immediately - any name that doesn't rely on a template parameter, directly or indirectly. These names are processed immediately when you define the template. Dependent names, on the other hand, cannot be resolved immediately; they are stored and then, when instantiation is performed, looked up in the template's context, but also in the context in which the template was instantiated for argument-dependent lookup only.
Here's an example:
void foo (int);
template <typename T> void bar(T t) {
foo(1.0);
foo(t);
}
void foo (double);
struct qux {};
void foo (qux);
void baz () {
bar (1.0);
qux q;
bar (q);
}
N.B. I know I got the metasyntactic names in the wrong order. I apologize, but I added qux last and couldn't be bothered to rewrite my comment.
The instantiations of the bar template each call foo twice. The first call is non-dependent, so the compiler resolves it as soon as it sees it. The result is that it calls foo (int), applying a conversion, even though it will later find a better definition. This is no different from any other function call in C++. The tricky bit comes with the second call, which is dependent. The first call in baz calls bar<double>, the latter calls bar<qux>. The instantiated bar attempts to call foo with an object of type T. In the double scenario, since primitives never use argument-depedent lookup, the result is once again looked up only from bar, and foo(int) is found. When called with qux, however, argument-dependent lookup is applied both in the the definition and instantiation context**, so foo(qux) is called.
It can be a tad stupid, but it tends to Do The Right Thing. Also, I hope you actually understood that; it can be rather confusing. You'll need to read that Wikipedia link to understand fully.
* MSVC may implement a lesser form of two-phase name lookup where it does resolve non-dependent names correctly, but it takes into account definitions after the template for dependent names. I forget whether it does this or omits two-phase lookup entirely and I don't have a copy of the program to check.
** In nearly every case, the instantiation context includes every declaration the definition context does. There is, however, the export keyword which can cause this not to be the case. That keyword is only implemented in one compiler frontend - I wonder why nobody else has implemented it? [/sarcasm]