Dependent template names and C++20 ADL
Asked Answered
H

1

7

Consider the following example:

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

Both GCC and Clang fail to compile this code under C++17 standard (with -Werror), because (according to my understanding) in C++17 ADL doesn't work when explicit template arguments <...> are present (unless a name is already established as a template name), so foo is a non-dependent name that is not found.

In C++20, the ADL rules have changes, and explicit template arguments don't prevent ADL. Now it seems that foo becomes a dependent name that should be resolvable via ADL. However, GCC and Clang have different opinions about the validity of this code. CLang compiles it without errors, but GCC (10.2, -std=c++2a) complains:

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

In C++17 mode, Clang produces the following warning:

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

Demo.

I have three related questions:

  1. Which compiler is right and why?
  2. In C++17, is foo in foo<0>(c) considered as a dependent name?
  3. In C++20, is foo in foo<0>(c) considered as a dependent name?
Harquebus answered 9/12, 2020 at 21:27 Comment(1)
Fwiw: gcc trunc in C++20 mode compiles it too so it seems they added that feature after 10.2.Salenasalene
B
13

This is P0846, which is a C++20 feature. It appears that gcc does not implement this yet.

It's not a question of dependent name or not, it's a question of does the compiler know that foo refers to a template or not, and so is foo< doing a comparison or is it starting to do template parameters.

In C++17, the compiler had to already know that foo was a template-name (which you could accomplish by adding using N::foo;) in order to perform ADL, in C++20 that's no longer true - now the rule is that if unqualified lookup finds a template or nothing, we also consider it to be a template.


The dependence of foo didn't change as a result of this paper. In foo<0>(c);, foo is still a dependent name. The rule in [temp.dep] from C++17 was:

In an expression of the form:

postfix-expression ( expression-listopt )

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.

The second bullet applies here - c is type-dependent. The C++20 wording is the same. The issue here wasn't that foo wasn't dependent in C++17. It's just that the rule was when we foo<, we don't know that foo is a template, so it's considered the less-than-operator, and that then fails.

Bicipital answered 9/12, 2020 at 21:44 Comment(2)
But is foo a dependent name? If all names fall into two categories, this should be a valid question.Harquebus
@Harquebus In C++17 it's also dependent... if lookup happened to find a template such that this expression could parse correctly. The "dependency" of the name didn't change as a result of this paper.Bicipital

© 2022 - 2024 — McMap. All rights reserved.