CS 70

Templates vs. Overloading

  • Duck speaking

    Templates provide one way to have a family of functions with the same name. But so does overloading. How do the two things interact?

  • Hedgehog speaking

    Oh, no, I hadn't even thought of that…

  • LHS Cow speaking

    Let's take a look…

Look over this code, and see if you can figure out what it will do…

#include <iostream>
#include <string>

template <typename T>
void print(const T& val) {
    std::cout << "templated print: " << val << std::endl;
}

void print(int val) {
    std::cout << "int print: " << val << std::endl;
}

void print(double val) {
    std::cout << "double print: " << val << std::endl;
}

int main()
{
    print(42);
    print(3.1415);
    print("Hello");

    return 0;
}

Once you've decided what you think will be printed (or what compiler errors you might see),

Describe what happened. Was it what you expected?

The code prints

int print: 42
double print: 3.1415
templated print: Hello

So we can see that overloading and templates work well together. If there is an overloaded function the compiler can call, it will use that; if not, it will use the function template to stamp out a custom function for the given type.

Now we're going to remove the function that takes a double. What do you think will happen? There are two possibilities we can imagine—the compiler will

  1. Perform type conversion to turn the double into an int and use the int version of the function; or,.
  2. Use the template to stamp out a custom function that works on doubles.

Which one do you think it'll do? Come to a conclusion, then click the Fork button in OnlineGDB and remove the print(double) function (or comment it out) and rerun the program to see what actually happens.

Describe what happened. Was it what you expected?

It prints

int print: 42
templated print: 3.1415
templated print: Hello

We can see that C++ prefers to use the function template over performing type conversion.

So when it comes to choosing a function, a template match for an argument is considered better than a type conversion (or promotion), but worse than an exact match for the type.

As our final change, let's try adding this function:

void print(std::string val) {
    std::cout << "string print: " << val << std::endl;
}

Once again, decide what you think will happen, and then run the code to see if it matches your expectations.

Describe what happened. Was it what you expected?

It still prints

int print: 42
templated print: 3.1415
templated print: Hello
  • Hedgehog speaking

    I'm relieved that your answer matches mine, but I'm still super confused. Why isn't the compiler calling the print() function that takes a string? Did we not define the function properly?

  • LHS Cow speaking

    You could investigate a bit more by seeing what happens if you leave the string version of the function alone, but comment out the function template.

If we comment out the function template, it does use our new function:

int print: 42
int print: 3
string print: Hello

Which tells us something interesting: that type conversion must be happening when we call print("Hello").

  • Horse speaking

    Woah! Woah! Isn't "Hello" a string?

  • Bjarne speaking

    No, it isn't, at least not a C++ std::string. It is a C-style string, which has type const char*, a pointer to the first character in an array of characters.

If you really want a genuine std::string literal, rather than a C-style string, you need to say

using namespace std::string_literals;

print("Hello"s);

The s on the end says we want a C++ std::string. We almost never actually write this form, however, because the std::string class provides automatic conversion from C-style strings to C++ ones.

  • Duck speaking

    Classes can provide automatic type conversion to/from other types?

  • Bjarne speaking

    Yes.

  • Pig speaking

    I want to know MORE about that!!

  • LHS Cow speaking

    Okay…

(When logged in, completion status appears here.)