简体   繁体   English

为什么 ADL 找不到函数模板?

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

What part of the C++ specification restricts argument dependent lookup from finding function templates in the set of associated namespaces? C++ 规范的哪一部分限制参数相关查找在相关命名空间集中查找函数模板? In other words, why does the last call in main below fail to compile?换句话说,为什么下面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.
}

This part explains it:这部分解释了它:

C++ Standard 03 14.8.1.6 : C++ 标准 03 14.8.1.6

[Note: For simple function names, argument dependent lookup (3.4.2) applies even when the function name is not visible within the scope of the call. [注意:对于简单的函数名称,即使函数名称在调用范围内不可见,参数相关查找 (3.4.2) 也适用。 This is because the call still has the syntactic form of a function call (3.4.1).这是因为调用仍然具有函数调用(3.4.1)的语法形式。 But when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call.但是,当使用带有显式模板参数的函数模板时,调用不具有正确的句法形式,除非在调用点存在具有该名称的函数模板可见。 If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply.如果没有这样的名称可见,则调用在语法上不是格式正确的,并且依赖于参数的查找不适用。 If some such name is visible, argument dependent lookup applies and additional function templates may be found in other namespaces.如果某些此类名称可见,则应用依赖于参数的查找,并且可能会在其他命名空间中找到其他函数模板。

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
}

Since c++20, adl works also fine with explicit function template.从 c++20 开始,adl 也适用于显式函数模板。 Here is the proposal: P0846R0: ADL and Function Templates that are not Visible :这是提案: P0846R0:不可见的 ADL 和函数模板

Instead of requiring the user to use the template keyword, a revision to the lookup rules was proposed so that a name for which a normal lookup produces either no result or finds one or more functions and that is followed by aa "<" would treated as if a function template name had been found and would cause ADL to be performed.不要求用户使用模板关键字,而是提出了对查找规则的修订,以便正常查找不会产生结果或找到一个或多个函数且后跟 aa "<" 的名称将被视为如果已找到函数模板名称并且会导致执行 ADL。

Currently, only GCC 9 has implement this feature, so your example can compile.目前,只有 GCC 9 实现了此功能,因此您的示例可以编译。

live demo . live demo

I would like to refine slightly accepted answer.我想改进稍微接受的答案。 It is not clear in the OP question, but the important part from the standard (cited by Kornel) is this (emphasis mine):在 OP 问题中不清楚,但标准(由 Kornel 引用)的重要部分是这个(强调我的):

But when a function template with explicit template arguments is used, the call does not have the correct syntactic form但是当使用带有显式模板参数的函数模板时,调用没有正确的语法形式

so what is prohibited is relying on ADL and using explicit template arguments.所以被禁止的是依赖 ADL 并使用显式模板参数。 Unfortunately using non-type template arguments requires using explicit arguments (unless they have default values).不幸的是,使用非类型模板参数需要使用显式参数(除非它们具有默认值)。

Below is sample code showing this.:以下是显示此内容的示例代码。:

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

Edit: No, this is not right.编辑:不,这是不对的。 See @Kornel's answer .请参阅@Kornel 的回答


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.我不完全确定,但在咨询了 Stroustrup 的“C++ 编程语言”后,我认为附录 C 第 13.8.4 节可能是原因。

Since frob is a template one could conceivably specialise it for i=0 at a point after you call it.由于frob是一个模板,因此可以想象,在您调用它之后的某个时刻,它可以专门用于i=0 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.这意味着实现将有两种可能的方式来选择调用哪个frob ,因为它可以在实例化点在处理翻译单元结束时选择它。

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*/ }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM