为什么ADL没有找到功能模板?

时间:2010-06-01 22:00:05

标签: c++ argument-dependent-lookup name-lookup template-function

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.
}

4 个答案:

答案 0 :(得分:82)

这部分解释了它:

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
}

答案 1 :(得分:4)

我想稍微接受一点答案。在OP问题中尚不清楚,但标准中的重要部分(由Kornel引用)是这个(强调我的):

  

但是当使用带有 显式模板参数 的函数模板时,调用没有正确的语法形式

所以禁止的是依赖于ADL并使用显式模板参数。不幸的是,使用非类型模板参数需要使用显式参数(除非它们具有默认值)。

以下是显示此内容的示例代码:

[live]

#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);
}

答案 2 :(得分:2)

自c ++ 20起,adl在显式函数模板中也可以正常工作。这是建议: P0846R0: ADL and Function Templates that are not Visible

  

不是要求用户使用template关键字,而是提出了对查找规则的修订,以便普通查找不会为其产生结果或找到一个或多个函数的名称,其后跟随一个aa“ <”将被视为已找到功能模板名称,并导致执行ADL。

当前,只有GCC 9实现了此功能,因此您的示例可以编译。

live demo

答案 3 :(得分:0)

编辑:不,这不对。请参阅@Kornel's answer


我不完全确定,但是已经咨询过Stroustrup的“The 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*/ } } ,因为它似乎可以在处理翻译单元结束时在实例化处选择它

所以,我认为你可以做的问题是

{{1}}