编译器如何在下面的代码中通过ADL找到模板函数X :: max(T const&,T const&)?

Wak*_*zil 5 c++ templates language-lawyer argument-dependent-lookup c++11

该标准的引用表示赞赏.

#include <iostream>

namespace X {
    class A {};
}

template <typename T>
inline T const& max(T const& a, T const& b, T const& c)
{
    return max(max(a, b), c);
}

inline X::A const& max(X::A const& a, X::A const& b)
{
    std::cout << "non-template" << '\n';
    return a;
}

int main()
{
    X::A a, b, c;
    max(a, b, c);
}

namespace X {
    template <typename T>
    inline T const& max(T const& a, T const& b)
    {
        std::cout << "template" << '\n';
        return a;
    }
}
Run Code Online (Sandbox Code Playgroud)

实例

Tem*_*Rex 3

标准语

\n\n

示例中的调用max()需要一个从属名称,因为它的参数取决于模板参数T。标准中定义了此类从属名称的两阶段名称查找,如下所示:

\n\n

14.6.4.2 候选函数 [temp.dep.candidate]

\n\n
\n

1 对于 post\xef\xac\x81x 表达式是从属名称的函数调用,\n 使用通常的查找规则(3.4.1,\n 3.4.2)找到候选函数,除了:

\n\n

\xe2\x80\x94 对于使用 unquali\xef\xac\x81ed 名称查找 (3.4.1) 的查找部分,仅找到来自模板 de\xef\xac\x81nition\n 上下文的函数声明。

\n\n

\xe2\x80\x94 对于使用关联命名空间 (3.4.2) 的查找部分,仅在模板 de\xef\xac\x81nition 上下文或模板实例化上下文中找到\n 函数声明。

\n
\n\n

不合格的查找定义为

\n\n

3.4.1 Unquali\xef\xac\x81ed 名称查找 [basic.lookup.unqual]

\n\n
\n

1 在 3.4.1 列出的所有情况下,将按照每个相应类别中列出的顺序在范围中搜索声明; 一旦找到名称的声明,名称查找就会结束。如果未找到\n 声明,则程序格式错误。

\n
\n\n

和参数相关的查找(ADL)为

\n\n

3.4.2 参数相关名称查找 [basic.lookup.argdep]

\n\n
\n

1 当函数调用 (5.2.2) 中的后缀表达式是\n unqualified-id时,可能会搜索在通常的\n 非限定查找 (3.4.1) 期间未考虑的其他命名空间,并且在这些命名空间中,\n可能会发现命名空间范围的友元函数或函数模板声明\n (11.3) 不可见。对搜索的这些修改取决于参数的类型(对于模板模板参数,则取决于模板参数的命名空间)。

\n
\n\n

为什么你的代码失败

\n\n

在您的示例中,非限定查找和 ADL在定义点都没有找到任何重载,因为编译器尚未看到任何双参数max()。ADL 也适用于实例化点,此时,编译器已经看到了template max(T, T)唯一可以调用的两个参数。(不同之处在于模板实例化发生在整个翻译单元被解析之后)。

\n\n

更好的代码

\n\n

您应该通过将非模板max(X::A, X::A)重载放入其中namespace X并将template max(T, T)其移出来修复代码。

\n\n
#include <iostream>\n\n// generic code\n\ntemplate <typename T>\ninline T const& max(T const& a, T const& b)\n{\n    std::cout << "template" << \'\\n\';\n    return a;\n}\n\ntemplate <typename T>\ninline T const& max(T const& a, T const& b, T const& c)\n{\n    using ::max; // fallback if no user-defined max\n    return max(max(a, b), c);\n}\n\n// X specific code\n\nnamespace X {\n\nclass A {};\n\ninline X::A const& max(X::A const& a, X::A const& b)\n{\n    std::cout << "non-template" << \'\\n\';\n    return a;\n}\n\n} // namespace X\n\nint main()\n{\n    X::A a, b, c;\n    max(a, b, c);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

打印“非模板”两次的实时示例。

\n