86

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

4 回答 4

88

这部分解释它:

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
}
于 2010-06-01T22:18:24.253 回答
11

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

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

目前,只有 GCC 9 实现了这个特性,所以你的例子可以编译。

live demo.

于 2018-12-30T02:37:44.557 回答
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);
}
于 2016-08-30T09:05:52.177 回答
0

Edit: No, this is not right. See @Kornel's answer.


I'm not entirely sure but having consulted Stroustrup's "The C++ programming language" I think that Appendix C section 13.8.4 might be the cause.

Since frob is a template one could conceivably specialise it for i=0 at a point after you call it. This means that the implementation would be left with two possible ways of choosing which frob to call as it appears it can choose it at the point of instantiation or at the end of processing the translation unit.

So, I think the problem is you could do

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*/ }
}
于 2010-06-01T22:40:37.193 回答