Gotw #30 name search (Name Lookup)
Difficulty: 9.5/10
Which one is called when you call a function? The answer depends on "Name Search", but you will surely find the details very surprising.
Problem
In the following code, which function is called? Why? Analyze the impact.
Namespace {
Struct X;
Struct y;
Void F (INT );
Void g (x );
}
Namespace B {
Void F (int I ){
F (I); // which F ()?
}
Void G (A: X ){
G (x); // which G ()?
}
Void H (A: Y ){
H (y); // which H ()?
}
}
Answer
In the following code, which function is called? Why?
Two of them are clear, but the third one needs to be proficient in the Name Search rules of C ++-especially Koenig lookup.
Namespace {
Struct X;
Struct y;
Void F (INT );
Void g (x );
}
Namespace B {
Void F (int I ){
F (I); // which F ()?
}
This F () calls itself and is infinite recursion.
In namespace A, there is also an F (INT) function. If B writes "using namespace A;" or "using a: F;", A: F (INT) will be used to search for F (INT) one of the candidate functions in the process, the "f (I)" call will result in ambiguity between A: F (INT) and B: F (INT. B Does not include a: F (INT) into its spatial range, so only B: F (INT) is considered, so there is no ambiguity in the call.
Void G (A: X ){
G (x); // which G ()?
}
This call has two meanings between A: G (x) and B: G (x. The programmer must use the namespace name to specify which G () to call ().
You may wonder why you need to do this. After all, like F (), B does not use the "using" command to bring a: G (x) into its spatial range. Therefore, you may think that only B :: g (x) is optional. That's right? Well, this is a fact. A supplementary rule for name search:
Koenig Lookup (Simplified Version)
If you provide a class-type real parameter (A: X) to the function, the compiler considers functions of the same name in a namespace that contains real parameters as optional functions.
The following is more complex, but the essence is the same. This is an example taken from the C ++ standard:
Namespace NS {
Class t {};
Void F (t );
}
NS: T parm;
Int main (){
F (parm); // OK, callns: F
}
I will not go into the necessity of Koenig lookup here (to extend your thinking, replace "ns" with "STD" and "T" with "string, "F" is replaced by "operator <, and then the process is considered ). Find more knowledge about Koenig lookup in the final "extended book" and its impact on name isolation and dependency between analysis classes. You must acknowledge that Koenig lookup is actually a good thing, and you must understand how it works, because it will affect the code you write at some time.
Void H (A: Y ){
H (y); // which H ()?
}
}
There is no other H (A: Y) function, so here f () calls itself, which is also infinite recursion.
Although the prototype of B: H () uses a type in namespace A, this does not affect the name search results, because namespace A does not conform to H (A: Y) the name of the function.
Analyze impact
Yes, the meaning of the Code in namespace B is affected by the function stated in the fully independent namespace A. Although B simply mentions a type found in, I didn't do anything, even "using!
This means that there is no dependency between namespaces as people initially imagined. Don't rush to condemn namespaces; namespaces are perfectly independent from each other, and only one T type is used to establish connections between them. In this issue, gotw just wants to point out such a special case. At this time, the namespace is not airtight... in addition, in this case, the namespace should not be airtight, and "extended books" will show this.
Extended books
The effect between namespace and Koenig lookup is far more profound than I have shown. I want to know why Koenig lookup is not a namespace vulnerability and how it affects our analysis of dependencies between classes, see what's in a class-the interface principle, an article I posted on March 1998 in C ++ report.