依赖模板名称和 C++20 ADL

Dependent template names and C++20 ADL

考虑以下示例:

namespace N {
    template<class>
    struct C { };

    template<int, class T>
    void foo(C<T>);
}

template<class T>
void bar(N::C<T> c) {
    foo<0>(c);
}

int main() {
    N::C<void> c;
    bar(c);
}

GCC 和 Clang 都无法在 C++17 标准下(使用 -Werror)编译此代码,因为(根据我的理解)在显式模板参数时,C++17 ADL 不起作用<...> 存在(除非已将名称建立为模板名称),因此 foo 是未找到的非相关名称。

在 C++20 中,ADL 规则发生了变化,显式模板参数不会阻止 ADL。现在看来 foo 成为一个依赖名称,应该可以通过 ADL 解析。但是,对于这段代码的有效性,GCC 和 Clang 有不同的看法。 CLang 编译它没有错误,但是 GCC (10.2, -std=c++2a) 抱怨:

error: 'foo' was not declared in this scope; did you mean 'N::foo'?

在 C++17 模式下,Clang 会产生以下警告:

warning: use of function template name with no prior declaration in function call with explicit template arguments is a C++20 extension

Demo.

我有三个相关问题:

  1. 哪个编译器是正确的,为什么?
  2. 在 C++17 中,foo<0>(c) 中的 foo 是否被视为从属名称?
  3. 在 C++20 中,foo<0>(c) 中的 foo 是否被视为从属名称?

这是 P0846,这是 C++20 的特性。看来 gcc 还没有实现这个。

这不是依赖名称的问题,而是编译器是否知道 foo 引用模板的问题, foo< 是否进行比较的问题开始做模板参数了。

在 C++17 中,编译器必须已经知道 foo 是一个模板名称(您可以通过添加 using N::foo; 来完成)以便在 C+ 中执行 ADL +20 这不再是真的 - 现在的规则是,如果非限定查找找到一个模板 或者什么都找不到 ,我们也认为它是一个模板。


foo 的依赖性并未因本文而改变。在foo<0>(c);中,foo仍然是从属名。 C++17 [temp.dep] 中的规则是:

In an expression of the form:

postfix-expression ( expression-list<sub>opt</sub> )

where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if

  • any of the expressions in the expression-list is a pack expansion,
  • any of the expressions or braced-init-lists in the expression-list is type-dependent, or
  • the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.

第二点适用于此 - c 取决于类型。 C++20 的措辞是相同的。这里的问题不是 foo 不依赖于 C++17。只是规则是当我们foo<时,我们不知道foo是一个模板,所以它被认为是小于运算符,并且然后失败。