繁体   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