larsch
larsch

Reputation: 981

Overload resolution of function dependent on template parameter

In the example below, a function template (quartic) uses an overloaded global function (square). When compiled, only overloads that are declared before the template itself are considered, so square(int) is used rather than square(double), even though square(double) is declared before the template is used.

int square(int);

template<typename T>
T quartic(T value) {
    return square(square(value));
}

double square(double);

double x(double y) {
    return quartic(4.0); // Uses square(int), wanted square(double)
}

Is there a way to make the resolution of which overloaded version of square depend on what is available when the template is used, so that the user can implement square for any type T he uses?

Note: Oddly MSVC will use square(double), while GCC, clang and icc uses square(int).

Upvotes: 0

Views: 301

Answers (2)

RedFog
RedFog

Reputation: 1015

the square in function template quartic is a dependent name, which means it depends on the template argument T and its lookup is postponed until T is known. besides, it's an unqualified name (a qualified name looks like std::cout), which means it will be looked up in not only its namespace but the namespace of the arguments, which is called argument-dependent lookup, ADL.

so in this case:

  • non-ADL lookup examines function declarations with external linkage that are visible from the template definition context. (the reason is explained in [1])
  • ADL examines function declarations with external linkage that are visible from either the template definition context or the template instantiation context.

obviously, it's not permitted to let your example work by non-ADL lookup. and neither do ADL, because double is not considered by ADL.

but if the arguments are user-defined type, it works by ADL. more clearly, if you want to call a function defined behind the this scope, you can make that function visible through ADL, which means the function and the arguments' type (or indirect type, see [2]) are in the same namespace.

references:

  1. https://en.cppreference.com/w/cpp/language/dependent_name
  2. https://en.cppreference.com/w/cpp/language/adl

Upvotes: 5

Ted Lyngmo
Ted Lyngmo

Reputation: 117168

Only functions declared before quartic are supposed to be considered, so MSVC does the wrong thing. One possible way to get around this would be to make square a function template too and let users provide specializations for it.

Your code:

template<typename T> T square(T);

template<typename T>
T quartic(T value) {
    return square(square(value));
}

User code:

template<> int square(int value) {
    return value * value;
}

template<> double square(double value) {
    return value * value;
}

Upvotes: 1

Related Questions