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

Hug*_*ugh 79 c++ template-function name-lookup argument-dependent-lookup

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.
}
Run Code Online (Sandbox Code Playgroud)

Kor*_*icz 83

这部分解释了它:

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
}
Run Code Online (Sandbox Code Playgroud)

  • @LightnessRacesinOrbit Vandevoorde和Josuttis的9.3.5节解释了为什么这个*是一个语法问题(OP的例子采用了命名):"编译器无法确定`f <3>(b)`是一个函数调用参数,直到它有决定`<3>`是一个模板参数列表.相反,我们不能确定`<3>`是一个模板参数列表,直到我们发现`f()`是一个模板.因为这个鸡和蛋问题不能要解决,表达式被解析为`(f <3)>(b)`,这没有任何意义." 请注意,这类似于成员函数模板的`template`消歧语法. (22认同)
  • 这是什么理由?似乎是一个奇怪的要求.我的意思是,句法形式与任何东西有什么关系? (9认同)
  • 有没有解决这个问题的建议?`模板f <3>(b)`可能是更好的语法? (8认同)
  • 此要求已在 C++20 中取消,OP 的代码现在格式良好:) (3认同)

陳 力*_*陳 力 9

从 c++20 开始,adl 也适用于显式函数模板。这是提案: P0846R0:不可见的 ADL 和函数模板

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

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

live demo.


mar*_*inj 5

我想改进稍微接受的答案。在 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);
}
Run Code Online (Sandbox Code Playgroud)