C++中一個名字查詢的小知識

fibercode發表於2018-07-13

最近看C++標準中的3.4 name lookup章節,碰巧的是stackoverflow也有人提問,他們對這個問題很疑惑,回答的也並不好。
https://stackoverflow.com/questions/25672745/friend-declarations-is-this-a-bug-in-clang

其中C++標準中$3.4.1章節有一個例子:

typedef int f;
namespace N {
     struct A {
        friend void f(A &);
        operator int();
        void g(A a) {
            int i = f(a);
            // f is the typedef, not the friend
            // function: equivalent to int(a)
        }
    };
}

標準上的註解的意思是f最終是一個typedef, 不會使用friend void f(A&)這個函式。

我自己的理解是:

  1. 在類中宣告的friend函式在普通查詢中,是不可見的,除非在類宣告之前有正式的宣告,也就是說friend宣告不能代替正式的宣告,這是標準所規定的。既然是不可見的,就不會hide掉那個typedef的f。標準$3.3.10講的name hiding在這裡不起作用。
  2. 名字查詢要是從普通查詢開始的,普通查詢就是從內到外一層層地找,找到一個名字,立馬停止。在這裡找到了f是typedef。普通查詢完成後才有ADL查詢。
  3. 因為2找到的是typedef,不是一個函式,所以ADL不會介入。ADL是C++11的新規則。如果函式呼叫是非限定的,例如不是: 某namespace::f(a), 某class::f(a), object_ptr->f(a), 或者object.f(a)之類的呼叫,並且引數是使用者定義型別,就會發起ADL,它把函式呼叫中的引數所屬的namespace也拉進來找這個函式f。所以ADL介入的條件是要麼普通查詢找不到這個名字或者找到了而且確實是個函式,那麼就把引數所屬的namespace也拉進來找,最後湊成一個函式集合,做過載解析規則選出最合適的函式。
    在這個例子中如果把typedef int f改成 int f()就會發生ADL。普通查詢首先會找到int f(),因為是函式,所以用ADL繼續找,找到void f(A&),編譯器會說”error: void value not ignored as it ought to be”。

這是由於ADL能找到只宣告為friend而沒有正式宣告的函式,標準就是這麼規定。並且void f(A&)比int f()更合適,但是void賦值給 i肯定出錯,所以編譯器報錯啦。


相關文章