Item 31. Name Lookup and the Interface Principle part 1

本文探讨了C++中函数调用时的名称查找规则,特别是Koenig查找机制。通过示例代码解释了如何确定调用哪个函数,并讨论了命名空间独立性的限制及其对程序设计的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Difficulty: 9½

When you call a function, which function do you call? The answer is determined by name lookup, but you're almost certain to find some of the details surprising.

In the following code, which functions are called? Why? Analyze the implications.

namespace A 
{
  struct X;
  struct Y;
  void f( int );
  void g( X );
}

namespace B
{
  void f( int i )
  {
    f( i );   // which f()?
  }
  void g( A::X x )
  {
    g( x );   // which g()?
  }
  void h( A::Y y )
  {
    h( y );   // which h()?
  }
}
 
I l@ve RuBoardPrevious Section Next Section

Solution

graphics/bulb_icon.gif

Two of the three cases are (fairly) obvious, but the third requires a good knowledge of C++'s name lookup rules梚n particular, Koenig lookup.

Let's start simple.

namespace A 
{
  struct X;
  struct Y;
  void f( int );
  void g( X );
}
namespace B
{
  void f( int i )
  {
    f( i );   // which f()?
  }

This f() calls itself, with infinite recursion. The reason is that the only visible f() is B::f() itself.

There is another function with signature f(int), namely the one in namespace A. If B had written "using namespace A;" or "using A::f;", then A::f(int) would have been visible as a candidate when looking up f(int), and the f(i) call would have been ambiguous between A::f(int) and B::f(int). Since B did not bring A::f(int) into scope, however, only B::f(int) can be considered, so the call unambiguously resolves to B::f(int).

And now for a twist:

void g( A::X x ) 
{
  g( x );   // which g()?
}

This call is ambiguous between A::g(X) and B::g(X). The programmer must explicitly qualify the call with the appropriate namespace name to get the g() he wants.

You may at first wonder why this should be so. After all, as with f(), B hasn't written "using" anywhere to bring A::g(X) into its scope, so you'd think that only B::g(X) would be visible, right? Well, this would be true except for an extra rule that C++ uses when looking up names:

 

Koenig Lookup[1] (simplified): If you supply a function argument of class type (here x, of type A::X), then to look up the correct function name the compiler considers matching names in the namespace (here A) containing the argument's type.

[1] Named after Andrew Koenig, who nailed down its definition and is a long-time member of both AT&T's C++ team and the C++ standards committee. See also Koenig97.

 

There's a little more to it, but that's essentially it. Here's an example, right out of the standard.

namespace NS 
{
  class T { };
  void f(T);
}
NS::T parm;
int main()
{
  f(parm);    // OK, calls NS::f
}

I won't delve here into the reasons why Koenig lookup is a Good Thing (if you want to stretch your imagination right now, take the above code and replace "NS" with "std", "T" with "string", and "f" with "operator<<" and consider the ramifications). See the next Item for much more on Koenig lookup and its implications for namespace isolation and for analyzing class dependencies.

Suffice it to say that Koenig lookup is, indeed, a Good Thing and that you should be aware of how it works, because it can sometimes affect the code you write.

Now back to a simple example:

  void h( A::Y y ) 
  {
    h( y );   // which h()?
  }
}

There is no other h( A::Y ), so this h() calls itself with infinite recursion.

Although B::h()'s signature mentions a type found in namespace A, this doesn't affect name lookup because there are no functions in A matching the name and signature h(A::Y).

So, what does it mean? That brings us to the last part of our Item question: Analyze the implications.

In short, the meaning of code in namespace B is being affected by a function declared in the completely separate namespace A, even though B has done nothing but simply mention a type found in A and there's nary a "using" in sight.

What this means is that namespaces aren't quite as independent as people originally thought. Don't start denouncing namespaces just yet, though; namespaces are still pretty independent, and they fit their intended uses to a T. The purpose of this Item is just to point out one of the (rare) cases in which namespaces aren't quite hermetically sealed梐nd, in fact, in which they should not be hermetically sealed, as the following Item will show.

Recall a traditional definition of a class:

 

A class describes a set of data, along with the functions that operate on that data.

 

Our question is: What functions are "part of " a class, or make up the interface of a class?

  •  
 
I l@ve RuBoardPrevious Section Next Section
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值