Templates[temp]

C++ templates: how to write function and class templates

Section
Templates [temp]
Standard
C++17
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
14 min read
In this answer11 clauses
  1. 1A function template: one pattern, one function per type
  2. 2How template arguments are deduced
  3. 3A class template, and class template argument deduction
  4. 4Non-type template parameters: a value in the type
  5. 5Alias templates, variable templates and default arguments
  6. 6Variadic templates and fold expressions
  7. 7Specialize a class template, overload a function template
  8. 8Why a template’s definition goes in the header
  9. 9typename, template and this-> inside a template
  10. 10Template errors, and what a concept changes
  11. 11Which template for which job

Abstract

A C++ template is a pattern from which the compiler generates a separate function or class for each set of template arguments a program uses. Write template <typename T> before a function or class; at a call, T is deduced from the arguments, so largest(readings) needs no angle brackets. Keep the whole definition in the header, or explicitly instantiate it in one .cpp file for each type you support, because the compiler needs the definition wherever the template is used.

A bar of four cells, two holding dashed empty slots, sends three arrows to three copies of the bar. In each copy both slots are filled: hatched in one, solid black in one, solid blue in one.
Figure 1 — A template is one pattern, from which the compiler makes a real function or class for each set of template arguments it is used with.

A C++ template is a pattern the compiler turns into a real function or class for each set of template arguments it is used with. Write template <typename T> before a function or class and use T as a type; at a call the compiler deduces T from the arguments, so largest(readings) needs no angle brackets. Put the whole definition in the header: the compiler needs it wherever the template is used. Since C++20, a concept says what T must support, and a type without it is rejected at the call, naming the concept.

This answer is written for C++17, which added class template argument deduction, auto non-type parameters and fold expressions; Since When’s C++17 table has the first GCC, Clang and MSVC with each. Function and class templates are older: C++11 added variadic and alias templates (C++11 table), C++14 variable templates (C++14 table). C++20 added concepts, auto parameters, double and class types as non-type parameters, and dropped typename where only a type can appear (C++20 table). C++26 adds pack indexing.

The samples were built with four compilers on three standard libraries: MSVC 19.44 and Clang 19.1 (MSVC STL) on Windows x64, where this Clang targets Microsoft’s compiler; GCC 16.2 with libstdc++ and Clang 23.1 (libc++) on Compiler Explorer, Linux x86-64. MSVC ran with /W4 /permissive-, the other three with -Wall -Wextra, all unoptimized and as C++17 unless the text names another standard. Every compiler that built a program printed the same output.

What changed: the requirements on T are written in a comment, and found in the error message as a concept in the declaration.

since C++20

A function template: one pattern, one function per type

A function template defines a family of functions (cppreference’s Templates page): T stands for a type until a call supplies one. largest returns the largest element of a vector whose elements compare with <:

[Example 1:
template <typename T>
T largest(const std::vector<T>& values) {
    T best = values.front();
    for (const T& v : values)
        if (best < v)
            best = v;
    return best;
}
// ...
std::vector<int> counts{3, 9, 4};
std::vector<double> readings{21.5, 23.0, 22.4};
std::vector<std::string> rooms{"hall", "attic", "porch"};

std::cout << largest(counts) << '\n';    // T = int: 9
std::cout << largest(readings) << '\n';  // T = double: 23
std::cout << largest(rooms) << '\n';     // T = std::string: porch

// a braced list says nothing about T: name it
std::cout << largest<double>({20, 21.5, 19}) << '\n';  // 21.5

— end example]

All four printed what the comments say.

The function template largest, a dashed blank for T, enters a band marked the compiler; arrows leave it for largest for int, double and std::string. A dotted grey box: never used, never generated.
Figure 2 — From one function template, the compiler writes a separate function for each set of template arguments the program uses, and none for a type it never uses.

Each instantiation is a separate function, with its own static variables: a static int calls added to largest counted call 1, call 2 for two largest(counts) calls, then call 1 again for largest(readings), on all four.

How template arguments are deduced

At a call, the compiler compares each function parameter’s type with its argument’s type and works out T ([temp.deduct.call]). With int i, const int ci, const int& ri and int arr[3], each row below is a static_assert on std::is_same_v that held on all four compilers:

Parameter Argument T becomes
T i, ci or ri int
T arr, an int[3] int*
T "hall" const char*
T& ci const int
T& arr int[3]
const T& i, ci or 42 int
T&& i int&
T&& ci const int&
T&& 42 or std::move(i) int
std::initializer_list<T> {1, 2, 3} int
const std::vector<T>& readings, a std::vector<double> double
  • By value, T loses top-level const and references, and an array or a function decays to a pointer.
  • Through T&, const stays in T and an array stays an array.
  • T&& is a forwarding reference when T is a parameter of the function template being called, deduced at the call: an lvalue makes T an lvalue reference. const T&& is not one, and neither is T&& in a member of a class template whose T is the class’s: Ring<double>::push(T&& value) rejected an lvalue double on all four.
  • A braced list is deduced only by a std::initializer_list<T> parameter or an array parameter such as const T (&)[N], which also takes N from the list’s length. largest({20, 21.5, 19}), whose parameter is a const std::vector<T>&, failed on all four (Clang: couldn't infer template argument 'T').

Each argument deduces T on its own, and no conversion is tried to make the results agree:

[Example 2:
template <typename T>
T largest_of(T a, T b) {
    return a < b ? b : a;
}
// ...
std::cout << largest_of(1, 2.5) << '\n';  // error: int or double?

— end example]

Both Clangs said:

[Example 3:
error: no matching function for call to 'largest_of'
note: candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')

— end example]

MSVC reported C2672 with template parameter 'T' is ambiguous, GCC no matching function for call to 'largest_of(int, double)'. largest_of<double>(1, 2.5) and largest_of(1.0, 2.5) both printed 2.5.

An explicit argument fixes T, and the argument must then convert to the parameter’s type as it stands: largest<long>(counts) on a std::vector<int> failed on all four, since no std::vector<int> converts to const std::vector<long>&.

A class template, and class template argument deduction

A class template is written the same way. A member defined outside the class repeats the template head and names the class Ring<T>:

[Example 4:
template <typename T>
class Ring {
public:
    explicit Ring(std::size_t capacity) : items_(capacity) {}

    void push(const T& value);  // defined outside the class
    std::size_t size() const { return count_; }
    const T& newest() const {
        return items_[(next_ + items_.size() - 1) % items_.size()];
    }

private:
    std::vector<T> items_;
    std::size_t next_ = 0;
    std::size_t count_ = 0;
};

template <typename T>
void Ring<T>::push(const T& value) {
    items_[next_] = value;
    next_ = (next_ + 1) % items_.size();
    if (count_ < items_.size())
        ++count_;
}
// ...
Ring<double> last3(3);
for (double r : {21.5, 23.0, 22.4, 24.1})
    last3.push(r);
std::cout << last3.size() << " readings, newest "
          << last3.newest() << '\n';  // 3 readings, newest 24.1

— end example]

A member template defined outside its class takes two template heads, the class’s first:

[Example 5:
template <typename T>
class Ring {
public:
    template <typename It>
    Ring(It first, It last);  // a member template
    std::size_t size() const { return items_.size(); }

private:
    std::vector<T> items_;
};

template <typename T>   // the class's parameters
template <typename It>  // then the member's own
Ring<T>::Ring(It first, It last) : items_(first, last) {}

— end example]

Built from three readings, it printed 3 readings on all four.

A member function of a class template is instantiated only when it is used, unless the whole class is explicitly instantiated: a Ring<Reading> compiled and ran on all four although its largest() member uses a < that Reading lacks, because nothing called largest(); with template class Ring<Reading>; added, all four rejected it at that <.

Since C++17 the compiler also deduces a class template’s arguments from the initializer. Each constructor gives an implicit deduction guide; a constructor that never names T, like this iterator pair, needs a guide written beside the class:

[Example 6:
template <typename T>
class Ring {
public:
    Ring(std::initializer_list<T> init) : items_(init) {}

    template <typename It>
    Ring(It first, It last) : items_(first, last) {}
// ...
// the iterator constructor never names T: say where T comes from
template <typename It>
Ring(It, It) -> Ring<typename std::iterator_traits<It>::value_type>;
// ...
Ring r{1, 2, 3};  // Ring<int>: the initializer_list constructor

std::vector<double> readings{21.5, 23.0, 22.4};
Ring copy(readings.begin(), readings.end());  // guide: Ring<double>
// braces prefer the initializer_list: a Ring of two iterators
Ring odd{readings.begin(), readings.end()};

— end example]

All four printed the sizes 3 3 2. Without the guide, Ring r{1, 2, 3}; still deduced Ring<int>, and copy failed on all four (Clang: no viable constructor or deduction guide for deduction of template arguments of 'Ring'). Under C++14 all four rejected Ring r{1, 2, 3}; (GCC: missing template arguments before 'r').

Non-type template parameters: a value in the type

A template parameter can be a value instead of a type. N fixes the capacity at compile time, and each N makes a different type:

[Example 7:
template <typename T, std::size_t N>
class Ring {
public:
    void push(const T& value) {
        items_[next_] = value;
        next_ = (next_ + 1) % N;
        if (count_ < N)
            ++count_;
    }
    std::size_t size() const { return count_; }
    static constexpr std::size_t capacity() { return N; }

private:
    std::array<T, N> items_{};
    std::size_t next_ = 0;
    std::size_t count_ = 0;
};
// ...
// N is part of the type: two sizes, two unrelated types
static_assert(!std::is_same_v<Ring<int, 4>, Ring<int, 8>>);

— end example]

A template <auto Limit> parameter (C++17) takes its type from the argument: <30> makes it an int, <40L> a long. C++20 also accepts a floating-point value and an object of a structural class: a literal class whose bases and non-static data members are all public, not mutable, and of structural types:

[Example 8:
template <double Limit>  // C++20: a floating-point parameter
bool over_limit(double reading) {
    return reading > Limit;
}

struct Band {  // public members of structural types
    double low;
    double high;
};

template <Band B>  // C++20: a class-type parameter
bool in_band(double reading) {
    return B.low <= reading && reading <= B.high;
}

— end example]

Under C++20, over_limit<30.5>(31.5) and in_band<Band{18.0, 26.0}>(21.5) printed true true on all four, Clang 19.1 included. Under C++17 all four rejected both parameters (Clang: a non-type template parameter cannot have type 'double' before C++20). A string literal cannot be the argument itself; strings as template parameters take the class-type route.

Alias templates, variable templates and default arguments

An alias template names a family of types, a variable template a family of values, and a default argument lets users leave a parameter out, as std::vector<double> leaves out its allocator:

[Example 9:
template <typename T>
using Readings = std::vector<T>;  // an alias template (C++11)

template <typename T>             // a variable template (C++14)
constexpr bool is_number = std::is_arithmetic_v<T>;

template <typename T, std::size_t N = 8>  // a default argument
struct Window {
    T slots[N];
};

— end example]

Used on one Readings<double>, is_number<double>, is_number<const char*> and the slots of a Window<int>, it printed 1 1 0 8 on all four.

Variadic templates and fold expressions

typename... Ts declares a parameter pack, any number of types. xs... expands it, sizeof...(xs) counts it, and a C++17 fold expression applies an operator across it:

[Example 10:
template <typename... Ts>
void log_all(const Ts&... xs) {
    std::cout << sizeof...(xs) << " values:";
    ((std::cout << ' ' << xs), ...);  // a comma fold: one << per value
    std::cout << '\n';
}

template <typename... Ts>
auto sum(const Ts&... xs) {
    return (xs + ...);  // x1 + (x2 + (x3 + ...))
}

template <typename... Ts>
double average(const Ts&... xs) {
    return sum(xs...) / sizeof...(xs);  // xs... expands the pack
}
// ...
log_all("hall", 21.5, 3);  // 3 values: hall 21.5 3
std::cout << sum(1, 2, 3) << '\n';               // 6
std::cout << average(21.5, 23.0, 22.4) << '\n';  // 22.3

— end example]

All four printed the three lines in the comments. Code older than C++17 recurses instead; this version also built under C++14:

[Example 11:
void log_all() { std::cout << '\n'; }  // no arguments left: stop

template <typename T, typename... Rest>
void log_all(const T& first, const Rest&... rest) {
    std::cout << ' ' << first;
    log_all(rest...);  // the same call, one argument fewer
}

— end example]

Over an empty pack, a unary fold has a value only for && (true), || (false) and the comma (void()). sum() with (xs + ...) failed on all four (Clang: unary fold expression has empty expansion for operator '+' with no fallback value); the binary fold (0 + ... + xs) returned 0.

To pass any number of arguments on unchanged, take them as Args&&..., a pack of forwarding references. ... repeats the whole pattern before it, so std::forward<Args>(args)... is one std::forward call per argument:

[Example 12:
template <typename... Args>
Reading& add(std::vector<Reading>& readings, Args&&... args) {
    // the pattern before ... is repeated for each argument
    return readings.emplace_back(std::forward<Args>(args)...);
}

— end example]

With a Reading built from a name and a value, add(readings, name, 20.5) and a call with a temporary std::string printed 2 hall on all four: name, an lvalue, was copied and kept its value. The single-argument case is in forwarding references and std::forward.

Since C++20 log_all can be written void log_all(const auto&... xs), with no template head: each auto in a parameter invents a template parameter. Under C++17, GCC 16.2 and Clang 23.1 compiled it with a warning; MSVC 19.44 and Clang 19.1 rejected it.

C++26 adds pack indexing, xs...[0] for a value and Ts...[0] for a type:

[Example 13:
template <typename... Ts>
Ts...[0] first(const Ts&... xs) {  // C++26: pack indexing, a type
    return xs...[0];                // C++26: pack indexing, a value
}

— end example]

GCC 16.2 and both Clangs compiled and ran it as C++26, and as C++23 with a warning (-Wc++26-extensions). MSVC 19.44 rejected it under /std:c++23preview and /std:c++latest alike (C2988). GCC 15 and Clang 19 were the first releases with it, by their status pages.

Specialize a class template, overload a function template

A class template can be specialized fully, every argument fixed, or partially, for a narrower set of arguments: some fixed, as in Ring<bool, N> below, or matched to a pattern such as T*. Here a Ring of bool stores bits:

[Example 14:
template <typename T, std::size_t N>
struct Ring {  // the primary template: N values in an array
    static constexpr const char* kind = "primary";
    std::array<T, N> items{};
};

template <std::size_t N>
struct Ring<bool, N> {  // partial: any N of bool, one bit each
    static constexpr const char* kind = "partial";
    std::bitset<N> bits;
};

template <>
struct Ring<bool, 1> {  // full: exactly one bool
    static constexpr const char* kind = "full";
    bool flag = false;
};

— end example]

Ring<double, 4>, Ring<bool, 64> and Ring<bool, 1> chose the primary, the partial and the full version on all four.

A function template can only be specialized fully: template <typename T> void log_value<T*>(T*) was rejected on all four (Clang: function template partial specialization is not allowed). A full specialization also takes no part in overload resolution. The compiler picks among the templates first, so which one a template <> line specializes depends on what was declared before it:

[Example 15:
namespace one {
template <typename T>
void log_value(T) { std::cout << "base template\n"; }

template <typename T>
void log_value(T*) { std::cout << "overload for pointers\n"; }

template <>  // specializes log_value(T*), the closer match
void log_value(int*) { std::cout << "specialization for int*\n"; }
}  // namespace one

namespace two {
template <typename T>
void log_value(T) { std::cout << "base template\n"; }

template <>  // specializes log_value(T): the only one declared yet
void log_value(int*) { std::cout << "specialization for int*\n"; }

template <typename T>
void log_value(T*) { std::cout << "overload for pointers\n"; }
}  // namespace two

namespace three {
// ...
void log_value(int*) { std::cout << "plain function for int*\n"; }
}  // namespace three
// ...
int reading = 21;
one::log_value(&reading);    // specialization for int*
two::log_value(&reading);    // overload for pointers
three::log_value(&reading);  // plain function for int*

— end example]

All four printed the three comments. Namespace three has both templates and a plain function, which overload resolution sees. That is Core Guidelines T.144: overload function templates, do not specialize them. Inside one function template, C++17 adds a third way to treat a type differently: if constexpr on a property of T keeps only the branch that applies, and the discarded branch is not instantiated.

Why a template’s definition goes in the header

The compiler instantiates a template where it is used and needs the definition there. A file that sees only the declaration compiles the call and leaves the function to the linker, which finds none. The sample declares, uses and never defines largest in one file, which gives the same error as a definition left in another .cpp:

[Example 16:
// largest.h: the declaration only; the definition is in largest.cpp
template <typename T>
T largest(const std::vector<T>& values);
// ...
std::vector<double> readings{21.5, 23.0, 22.4};
std::cout << largest(readings) << '\n';  // needs largest<double>

— end example]

The compile step printed nothing on any of the four. GCC’s linker said:

[Example 17:
undefined reference to `double largest<double>(std::vector<double, std::allocator<double> > const&)'
collect2: error: ld returned 1 exit status

— end example]

Clang 23.1 printed the same undefined reference; MSVC and Clang 19.1, through Microsoft’s linker, printed LNK2019: unresolved external symbol for largest<double>, then LNK1120. Clang’s -Wundefined-func-template, off by default, also warns at compile time: instantiation of function 'largest<double>' required here, but no definition is available; the link still fails.

The other way out keeps the definition in one .cpp file and instantiates it there for each type you support ([temp.explicit]):

[Example 18:
// ring.h
template <typename T>
class Ring {
public:
    explicit Ring(std::size_t capacity) : items_(capacity) {}
    void push(const T& value);  // defined in ring.cpp
// ...
// Ring<double> is instantiated in ring.cpp: not here
extern template class Ring<double>;
// ...
// ring.cpp
template <typename T>
void Ring<T>::push(const T& value) {
// ...
template class Ring<double>;  // every member of Ring<double>, here

— end example]

Kept in one file, the sample printed 3 readings on all four. The extern template line tells a file that includes it that the members of Ring<double> are instantiated elsewhere, so the compiler does not instantiate push there, even with its definition in sight. With push defined above the call and no template class Ring<double>; anywhere, all four failed to link on Ring<double>::push. Members defined inside the class are inline and may still be compiled in place, and the explicit instantiation definition must then exist somewhere in the program.

typename, template and this-> inside a template

Inside a template, a name that depends on a template parameter is taken to be neither a type nor a template ([temp.res]). typename and template say otherwise:

[Example 19:
template <typename C>
typename C::value_type largest(const C& readings) {
    typename C::value_type best = *readings.begin();
    for (const auto& r : readings)
        if (best < r)
            best = r;
    return best;
}

struct Sensor {
    template <typename U>
    U read() const { return U(21.5); }
};

template <typename S>
double sample(const S& sensor) {
    return sensor.template read<double>();  // read is a template
}

— end example]

A base class that depends on T is not searched for unqualified names, so its members are reached through this->:

[Example 20:
template <typename T>
class Storage {
protected:
    std::vector<T> items_;
};

template <typename T>
class Ring : public Storage<T> {  // a base that depends on T
public:
    void push(const T& value) { this->items_.push_back(value); }
    std::size_t size() const { return this->items_.size(); }
};

— end example]

Each keyword left out, under C++17; the typename and template cases did the same under C++20:

Left out GCC 16.2, Clang 23.1 MSVC 19.44 /permissive- Clang 19.1 (MSVC STL)
typename on a local variable’s type error error C7510 warning -Wtypename-missing
typename in a template argument error error C2275 warning -Wmicrosoft-template
template before read<double> error compiles, no warning warning, no flag
this-> before items_ error error C3861 warning -Wmicrosoft-template

GCC names the fix: need 'typename' before 'C::value_type' because 'C' is a dependent scope. Clang 19.1 accepts the omissions because it targets Microsoft’s compiler; with -fno-ms-compatibility the typename and this-> cases were errors. MSVC with /permissive in place of the conforming /permissive- compiled all four omissions.

C++20 drops typename where only a type can appear (P0634R3):

[Example 21:
template <typename C>
C::value_type largest(const C& readings) {  // C++20: a return type
    using value_type = C::value_type;      // C++20: an alias
// ...
template <typename C>
struct Summary {
    C::value_type high;                     // C++20: a member
    void add(C::const_reference value);     // C++20: its parameter
    auto as_int() const {
        return static_cast<C::size_type>(high);  // C++20: a cast
    }
};

— end example]

All four compiled it as C++20. As C++17, MSVC and GCC rejected it and both Clangs accepted it with a warning (-Wc++20-extensions). A local variable’s type and a template argument still need typename in C++20, and GCC 16.2, Clang 23.1 and MSVC 19.44 rejected both without it.

Template errors, and what a concept changes

Without a constraint, a type that lacks an operation is reported where the template’s code uses it. With largest’s own loop that is short: called on a vector of a Reading with no <, all four printed 7 lines, naming the missing <. The long errors come when the failing use is inside the library:

[Example 22:
template <typename T>
T largest(const std::vector<T>& values) {
    return *std::max_element(values.begin(), values.end());
}

struct Reading {  // no operator<
    double celsius;
};

int main() {
    std::vector<Reading> readings{{21.5}, {23.0}, {22.4}};
    std::cout << largest(readings).celsius << '\n';  // error
}

— end example]

Every compiler reported it in a standard library header; GCC and both Clangs first printed the chain of includes that leads there:

Compiler Reported in It says
MSVC 19.44 xutility C3889, no call operator of std::less<void> matches
Clang 19.1 xutility no matching call to std::less<void>
GCC 16.2 bits/stl_algo.h no match for call to (std::less<void>)
Clang 23.1 __algorithm/comp.h invalid operands to binary expression

A concept moves the first line to the call. With largest declared as template <std::totally_ordered T>, both Clangs began:

[Example 23:
error: no matching function for call to 'largest'
note: candidate template ignored: constraints not satisfied [with T = Reading]
note: because 'Reading' does not satisfy 'totally_ordered'

— end example]

MSVC began with C2672, 'largest': no matching overloaded function found, then the concept 'std::totally_ordered<Reading>' evaluated to false; GCC with no matching function for call to 'largest(std::vector<Reading>&)'. Counted in non-empty lines of output:

largest First error at MSVC 19.44 Clang 19.1 GCC 16.2 Clang 23.1
Its own loop the < in largest 7 7 7 7
std::max_element a library header 32 28 30 or more 22
std::totally_ordered the call 37 19 30 or more 19

Shorter it was not: against the 7 lines from largest’s own loop, the constrained version printed 19 lines on both Clangs, 37 on MSVC and 30 or more on GCC. std::totally_ordered checks == first, so every compiler named ==, which largest never uses, and MSVC listed every operator== in its headers, twice.

The concept asks for all six comparisons: a Reading with only operator< was rejected on all four (Clang: 'Reading' does not satisfy 'equality_comparable'; MSVC: the concept 'std::equality_comparable<Reading>' evaluated to false). A Reading with auto operator<=>(const Reading&) const = default; satisfied it, and the call printed 23.

Constrain with what you mean callers to provide, and expect the concept to check all of it. Concepts replace the std::enable_if and SFINAE tricks that did this before C++20; how to write and use C++20 concepts covers writing your own.

Which template for which job

You need Write Since
One function for many types a function template, or auto parameters before C++17; C++20 for auto
One class for many types a class template before C++17
A size or a value in the type a non-type template parameter before C++17; C++20 for double and classes
Any number of arguments a variadic template and a fold C++11; C++17 for folds
A named requirement on T a concept C++20
A function passed to an algorithm a lambda or a function object C++11 for lambdas
A choice among types at run time virtual functions or std::variant before C++11; C++17 for std::variant

For each template you write:

  • The definition in the header, or an explicit instantiation in one .cpp for each type you support.
  • Let the call deduce the arguments; name them only when nothing can be deduced or two arguments disagree.
  • Overload function templates; specialize class templates only (T.144).
  • From C++20, constrain with a concept that asks for what callers must provide, and nothing they need not.
  • typename and template where the compiler asks for them, this-> for a dependent base’s members.

Where class templates lead next: expression templates. More answers on templates are in Templates.

Questions

What is the difference between typename and class in a template?

None for a type parameter: template <class T> and template <typename T> declare the same thing. Before C++17 the standard required class in one place, a template template parameter such as template <template <typename...> class Container>; C++17 allowed typename there too (N4051). Under C++14, MSVC 19.44, GCC 16.2, Clang 19.1 and Clang 23.1 all accepted typename there anyway, and both Clangs warned that it is a C++17 extension.

Can I put a template’s definition in a .cpp file?

Only if that .cpp file also explicitly instantiates the template for every set of arguments the program uses, with a line such as template class Ring<double>;. Otherwise a file that calls the template sees only its declaration: it compiles, and the program fails to link. MSVC reports LNK2019, an unresolved external symbol; the GNU linker reports an undefined reference.

What is the difference between a function template and a template function?

None in practice: both names are used for the same thing. The standard's term is function template, the pattern written after template <typename T>. What the compiler generates from it for one set of arguments, such as largest<int>, is a function, a specialization of the template; overload resolution still prefers a non-template function with the same signature to it.

Do templates make a C++ program slower?

Not by how they are called: which function a template call runs is decided at compile time, and the call goes straight to the function generated for those template arguments, with no run-time lookup. The cost is paid when building, and in size: up to one copy of each function for each set of arguments the program uses.

Can a virtual function be a template?

No: a member function template cannot be declared virtual. A class template can still have virtual member functions. Cpp Next’s answer on C++ virtual functions compiles both cases on the same four compilers.

What is CTAD in C++?

Class template argument deduction, added in C++17: the compiler deduces a class template's arguments from the initializer, so std::pair p(2, 4.5); declares a std::pair<int, double>. Each constructor yields an implicit deduction guide; a constructor that never names T, such as one taking two iterators, needs a deduction guide written beside the class. GCC 7, Clang 5 and Visual Studio 2017 15.7 were the first releases with it.

Read next

  1. C++ generic lambdas, template lambdas and function objectsTemplates · Miriam Castellanos · written for C++20
  2. C++ expression templates explained with a C++20 exampleTemplates · Miriam Castellanos · written for C++20
  3. Strings as C++ template parameters with C++20 fixed_stringTemplates · Miriam Castellanos · written for C++20