Why does ADL fail on a dependent typename?

57 Views Asked by At

I noticed that get<0>(t) fails to be resolved to std::get<0>(t) if its argument's type depends on a template parameter:

template <typename T>
void foo(T) {
  std::tuple<T> tup{0};
  get<0>(tup) = 1; // error: 'get' was not declared in this scope; did you mean 'std::get'?
}

This works fine if foo is not a template (i.e. if tup is declared as std::tuple<int>) or with using std::get;. Shouldn't it also work in a function template where ADL is often extra useful?

2

There are 2 best solutions below

3
bitmask On

Looks like a compiler error on gcc up to and including 10.2. Subsequent versions fix this.

1
Artyer On

This was changed in C++20 (P0846R0).

Before C++20, get<0>(tup) = 1 is parsed as get < 0 > (tup) = 1 (get is less than zero is greater than tup is assigned to one). Since get doesn't exist, this won't compile.

In C++20, a new rule was added so it's interpreted as a template-name when nothing is found, allowing it to be interpreted as calling a function template.

The pre-C++20 fix is to declare a function template in scope, even if it it won't ultimately be selected:

// Make it difficult to call this by accident
template<template<template<int> class> class>
void get() = delete;

template <typename T>
void foo(T) {
  std::tuple<T> tup{0};
  // lookup for `get` finds the global function template, so this is
  // parsed as a call to a template function
  get<0>(tup) = 1;
}