C++进阶--Koenig lookup 依赖于实参的名字查找

//############################################################################
/*
 * Koenig Lookup / Argument Dependent Lookup (ADL)
 */

// 例1: 什么是Koenig查找
namespace A
{
   struct X {};
   void g( X ) { cout << " calling A::g() 
"; }
}

// void g( X ) { cout << " calling A::g() 
"; } //如果再加一个函数,会编译不过

int main() {
   A::X x1;
   A::g(x);  //正常
   g(x1);   // 移除A::并不会报错,会到实参所在的命名空间查找
}


// 例2:
class C {
   public:
   struct Y {};
   static void h(Y) { cout << "calling C::h() 
"; }
};

int main() {
   C::Y y;
   h(y);  // Error Koenig查找只对命名空间有效,对类无效
}


// 例3:
namespace A
{
   struct X {};
   void g( X ) { cout << " calling A::g() 
"; }
}

class B {
   void g(A::X ) { cout << "calling B::g() 
" ; }
};

class C : public B {
   public:
   void j() {
      A::X x;
      g(x);
   }
};

int main() {
   C c;
   c.j();  //没有歧义,先在类中查找,没找到再去global和Koenig查找
}



// 名字隐藏的情况:命名空间的例子
namespace A
{
   struct X {};
   void g(X ) { std::cout << " calling A::g() 
"; }

   namespace C {
      void g( ) { std::cout << "calling C:g() 
" ; }
      void j() {
         //using A::g;
         g(8);  //编译不过,名字遮蔽
         X x;
         g(x);  //Koenig原则同样有效
      }
   }
}

int main() {
   A::C::j();
}


/*
 *  名字查找顺序
 *
 *  命名空间:
 *  当前作用域 => 外面一层作用域 => ... => 全局作用域 
 *
 *  要覆写顺序:
 *  1. 使用限定符或者using声明
 *  2. Koenig lookup
 *
 *  类:
 *  当前类作用域 => 父类作用类 => ... => 全局域
 *
 *  要覆写顺序::
 *   - 使用限定符或者using声明
 *
 *
 *  名字隐藏
 * 当高层作用域定义了和低层作用域相同名字的函数
 */



/*
 * 为啥要有Koenig Lookup?
 */

// 例1:
namespace A
{
   struct X {};
   void g( X ) { cout << " calling A::g() 
"; }
   void g( ) { cout << " calling A::g() 
"; }
}

int main() {
   A::X x1;
   g(x1);   // Koenig Lookup, or Argument Dependent Lookup (ADL)
   g();     // Error
}



/*
 * 1. 实际原因
 */

   std::cout << "Hi.
"; // 这里的数据运算符会调用 std::operator<<,而不用显式指定

   std::cout std::<< "Hi.
"; // 如果没有Koenig查找,要写成这种形式,编译不过
   std::operator<<(std:cout, "Hi,
");  //正确的写法



/*
 * 2. 理论原因: 
 *       -- 什么是类的接口?
 */

namespace A
{
   class C {
      public:
      void f() = 0;
      void g() = 0;
   };
   void h(C);  //是C的接口的一部分
   ostream& operator<<( ostream&, const C& );  //是C的接口的一部分
}
void j(C);  //不就是C的接口的一部分

/*
 * 类的定义: 
 * 	类是描述一组数据以及操作这些数据的函数的集合。
 */



/* 
 * 工程上的原则: 
 * 1. 对C类进行操作且跟C在同一个命名空间的函数是C的接口的一部分
 * 2. 所以作为C的接口的一部分的函数需要在同一个命名空间下
 */

   A::C c;
   c.f();    //可以直接调用
   h(c);    //可以直接调用





namespace A { 
   class C {}; 
   int operator+(int n, A::C) { return n+1; }  //如果放在外面可能会出现问题,万一std命名空间中定义了其他函数,就不会找到这个函数
}


int main()
{
   A::C arr[3]; 
   std::accumulate(arr, arr+3, 0);  // return 3
}

// 定义在C++标准库<numeric>
namespace std {
   template <class InputIterator, class T>
      T accumulate ( InputIterator first, InputIterator last, T init )
   {
     while ( first!=last )
       init = init + *first++; 
     return init;
   }
}

原文地址:https://www.cnblogs.com/logchen/p/10177021.html