簡體   English   中英

為什么 ADL 找不到函數模板?

[英]Why doesn't ADL find function templates?

C++ 規范的哪一部分限制參數相關查找在相關命名空間集中查找函數模板? 換句話說,為什么下面main中的最后一個調用無法編譯?

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
    void non_template(foo const&) {}
}

int main() {
    ns::foo f;
    non_template(f); // This is fine.
    frob<0>(f); // This is not.
}

這部分解釋了它:

C++ 標准 03 14.8.1.6

[注意:對於簡單的函數名稱,即使函數名稱在調用范圍內不可見,參數相關查找 (3.4.2) 也適用。 這是因為調用仍然具有函數調用(3.4.1)的語法形式。 但是,當使用帶有顯式模板參數的函數模板時,調用不具有正確的句法形式,除非在調用點存在具有該名稱的函數模板可見。 如果沒有這樣的名稱可見,則調用在語法上不是格式正確的,並且依賴於參數的查找不適用。 如果某些此類名稱可見,則應用依賴於參數的查找,並且可能會在其他命名空間中找到其他函數模板。

namespace A {
  struct B { };
  template<int X> void f(B);
}
namespace C {
  template<class T> void f(T t);
}
void g(A::B b) {
  f<3>(b);    //ill-formed: not a function call
  A::f<3>(b); //well-formed
  C::f<3>(b); //ill-formed; argument dependent lookup
              // applies only to unqualified names
  using C::f;
  f<3>(b);    //well-formed because C::f is visible; then
              // A::f is found by argument dependent lookup
}

從 c++20 開始,adl 也適用於顯式函數模板。 這是提案: P0846R0:不可見的 ADL 和函數模板

不要求用戶使用模板關鍵字,而是提出了對查找規則的修訂,以便正常查找不會產生結果或找到一個或多個函數且后跟 aa "<" 的名稱將被視為如果已找到函數模板名稱並且會導致執行 ADL。

目前,只有 GCC 9 實現了此功能,因此您的示例可以編譯。

live demo

我想改進稍微接受的答案。 在 OP 問題中不清楚,但標准(由 Kornel 引用)的重要部分是這個(強調我的):

但是當使用帶有顯式模板參數的函數模板時,調用沒有正確的語法形式

所以被禁止的是依賴 ADL 並使用顯式模板參數。 不幸的是,使用非類型模板參數需要使用顯式參數(除非它們具有默認值)。

以下是顯示此內容的示例代碼。:

[居住]

#include <string>
#include <utility>

namespace C {
  struct B { };
  template<class T> void f(T t){}
}

void g(C::B b) {
  f(b);           // OK
  //f<C::B>(b);   // ill-formed: not a function call, but only 
                  //  because explicit template argument were used

  std::string s;
  move(s);                      // OK
  //move<std::string&>(s);      // Error, again because 
                                //  explicit template argument were used
  std::move<std::string&>(s);   // Ok
}

int main()
{
 C::B b;
 g(b);
}

編輯:不,這是不對的。 請參閱@Kornel 的回答


我不完全確定,但在咨詢了 Stroustrup 的“C++ 編程語言”后,我認為附錄 C 第 13.8.4 節可能是原因。

由於frob是一個模板,因此可以想象,在您調用它之后的某個時刻,它可以專門用於i=0 這意味着實現將有兩種可能的方式來選擇調用哪個frob ,因為它可以在實例化點在處理翻譯單元結束時選擇它。

所以,我認為問題是你可以做

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
}

int main() {
    ns::foo f;
    frob<0>(f);
    return 0;
}

namespace ns {
    template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM