对于具有附加(非推导)模板参数的函数,ADL 失败(或未完成?)

ADL fails (or not done?) for function with additional (non deduced) template parameter

namespace N {
    class C {};

    template<typename X>
    char const * found(X && x) {
        return "found";
    }

    template<typename, typename X>
    char const * notfound(X && x) {
        return "not found";
    }
}

这定义了一个带有 class C 和两个函数模板的命名空间 Nfound 有一个模板参数,可以从函数参数中推导出来。 notfound 有一个无法推导的附加模板参数。

给定以下测试代码 (on ideone):

#include <iostream>
int main() {
    N::C object;
    std::cout
        << found(object) << std::endl
        << notfound<bool>(object) << std::endl  // ERROR
        << notfound<bool, N::C>(object) << std::endl; // ERROR
}

我假设 argument dependent lookup 会通过参数类型的最内层封闭命名空间(即 N)找到 foundnotfound N::C.

但是:

prog.cpp: In function ‘int main()’:
prog.cpp:21:6: error: ‘notfound’ was not declared in this scope
   << notfound<bool>(object) << std::endl
      ^~~~~~~~
prog.cpp:21:6: note: suggested alternative:
prog.cpp:12:15: note:   ‘N::notfound’
  char const * notfound(X && x) {
               ^~~~~~~~

(注释掉 notfound<bool>(object) 调用后 notfound<bool, N::C>(object) 的相同错误)

为什么通过ADL找不到notfound


背景: 我正在为一些包装器 class 实现一个 get 函数,总的来说与 std::get(std::tuple) 比较相似。包装器 class 作为一个实现细节存在于某个命名空间 lib::aspect::part::impl 中。出于显而易见的原因,我不希望库的用户指定 using lib::aspect::part::impl::get

因为对带有显式指定模板参数的函数模板的函数调用要求模板名称必须通过普通查找找到;直到 ADL 无法启动。

来自标准:.8.1/8 Explicit template argument specification [temp.arg.explicit]

(强调我的)

[ Note: For simple function names, argument dependent lookup applies even when the function name is not visible within the scope of the call. This is because the call still has the syntactic form of a function call ([basic.lookup.unqual]). 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. [ Example:

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
}

— end example ] — end note ]

最后一句给出了可能的解决方法;您可以在任何地方添加函数模板的声明,以使名称可见以便被调用。例如

template<typename>
void notfound();

int main() {
    N::C object;
    std::cout
        << found(object) << std::endl
        << notfound<bool>(object) << std::endl
        << notfound<bool, N::C&>(object) << std::endl; // btw the 2nd template argument should be N::C&
}

LIVE