Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ templated function overloading rules

When overloading a templated function, how should the compiler chose which version of the function to call if it has the option to either:

  • Call a templated version of the function (such as func<T>(foo)).
  • Call an overloaded version of the function which is not itself templated but where the type of the parameter being passed to the function inherits from the type specified in the overloaded function template.

Consider the following C++ code:

#include <stdio.h>

struct Parent {}; 
struct Child : public Parent {}; 

template <typename T>
void func(T) {
  printf("func(T)\n");
}

void func(Parent) {
  printf("func(Parent)\n");
}

int main() {
  func(1);
  func(Parent());
  func(Child());
}

Compiled with gcc or clang, this outputs:

func(T)
func(Parent)
func(T)

The first two lines are expected and make sense. However, in the call func(Child()), it could just as easily call func(Parent) (which seems like, if anything, what it should do).

As such, I have two main questions:

  • What are the exact rules laid out by the standard as to how to resolve such conflicts? There is some information in this question/answer, but if anything it conflicts with what I am observing.
  • Is there any way to force the compiler to call func(Parent) when passed a Child?

I can get around this requirement in my own code and this example is a simplified version of what I am trying to do, but I believe that it is the same problem.

like image 219
forkrul Avatar asked Jul 22 '15 12:07

forkrul


People also ask

How template functions can be overloaded?

You may overload a function template either by a non-template function or by another function template. The function call f(1, 2) could match the argument types of both the template function and the non-template function.

What is a templated function C++?

Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.

Can template be overloaded?

A function template can overload non-template functions of the same name. In this scenario, the compiler first attempts to resolve a function call by using template argument deduction to instantiate the function template with a unique specialization.

What is the big advantage that templated functions have over regular functions?

Templates are type-safe. They are generally considered as an improvement over macros for these purposes. Templates avoid some common errors found in code that make heavy use of function-like macros. Both templates and macros are expanded at compile time.


2 Answers

The rules for overload resolution go something like this:

  1. Find all the candidate functions by name
  2. Perform template deduction and prune down to the viable candidates (i.e. drop the calls that are ill-formed).
  3. Pick the best viable candidate via:

    a. Choose the one with the best conversion sequence (think of this as "doing the least necessary work to convert from the argument types to the parameter types")
    b. Choose the non-function template over the function template
    c. Choose the most specialized function template

Let's go into these on a case by case basis. For your function calls:

func(1);

After (2), we have one viable candidate, func<int>. func(Parent ) is not a viable candidate, since Parent is not constructible from int, so we're done and call the function template.

func(Parent());

We have two viable candidates: func<Parent> and func(Parent ). Both take the exact same arguments so the conversion sequences are identical. So we end up in step 3b: we choose the non-template over the template, and we call func(Parent ).

func(Child());

We have two viable candidates: func<Child> and func(Parent ). In the former case, the argument type is Child so it's an exact match for what we're passing in (no conversion necessary). In the latter case, the argument type is Parent so we'd have to perform a derived-to-base conversion. Since the function template has a better conversion sequence (i.e. no conversion necessary), it is considered the best viable overload. You could call func(Parent ) - that is a viable candidate, but it's not the best viable candidate. func<Child> is a better match.

Is there any way to force the compiler to call func(Parent) when passed a Child?

You could either cast the Child to a Parent yourself:

Child c;
func(static_cast<Parent>(c));

Or you could write another overload that takes a Child, which would be preferred only in the 3rd case (and only viable in the 3rd case):

void func(Child );

Or rewrite your function template so as to not take any class in that hierarchy:

template <typename T,
          typename = std::enable_if_t<
              !std::is_convertible<T*, Parent*>::value
          >>
void func(T );

The latter solution (called SFINAE) would remove func<Child> from the set of viable candidates, so that the only viable candidate becomes func(Parent ).

like image 187
Barry Avatar answered Nov 07 '22 14:11

Barry


In order to be able to get anything done, the standard invented a list of goodness, and the order in which different things would be tried. There are series of lectures on C9 from STL that go into this in length.

First, the template instantiates a solution for each of the three cases. The second version picks func(Parent) because it matches exactly, and wins over the template. The third call takes the templated version over a conversion which is considered "less good".

My only thought on preventing this would be to do some horrible SFINAE that tests for every T that it can't be inherited from Parent using type traits. Concepts in C++17 might allow for something slightly less convoluted.

See

Stephan T. Lavavej: Core C++, 2 of n - Template Argument Deduction

Stephan T. Lavavej: Core C++, 3 of n - Overload Resolution

like image 24
woolstar Avatar answered Nov 07 '22 12:11

woolstar