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
- 1A function template: one pattern, one function per type
- 2How template arguments are deduced
- 3A class template, and class template argument deduction
- 4Non-type template parameters: a value in the type
- 5Alias templates, variable templates and default arguments
- 6Variadic templates and fold expressions
- 7Specialize a class template, overload a function template
- 8Why a template’s definition goes in the header
- 9typename, template and this-> inside a template
- 10Template errors, and what a concept changes
- 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 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 <:
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.

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:: |
{1, 2, 3} |
int |
const std:: |
readings, a std::vector<double> |
double |
- By value,
Tloses top-levelconstand references, and an array or a function decays to a pointer. - Through
T&,conststays inTand an array stays an array. T&&is a forwarding reference whenTis a parameter of the function template being called, deduced at the call: an lvalue makesTan lvalue reference.const T&&is not one, and neither isT&&in a member of a class template whoseTis the class’s:Ring<double>::rejected an lvaluepush(T&& value) doubleon all four.- A braced list is deduced only by a
std::parameter or an array parameter such asinitializer_list<T> const T (&)[N], which also takesNfrom the list’s length.largest({20, 21.5, 19}), whose parameter is aconst std::, failed on all four (Clang:vector<T>& couldn't infer template argument 'T').
Each argument deduces T on its own, and no conversion is tried to make the results agree:
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:
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::.
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>:
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:
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:
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:
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:
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:
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:
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:
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:: is one std::forward call per argument:
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:
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:
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:
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:
// 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:
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]):
// 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:
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->:
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::. 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):
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:
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::, both Clangs began:
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::; GCC with no matching function for call to 'largest(std::. 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::). 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
.cppfor 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.
typenameandtemplatewhere 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.
Sources
- Templatescppreference.com
- Class template argument deduction (CTAD)cppreference.com
- Fold expressionscppreference.com
- [temp.deduct.call] Deducing template arguments from a function callC++ standard working draft
- [temp.explicit] Explicit instantiationC++ standard working draft
- [temp.res] Name resolutionC++ standard working draft
- T.144: Don’t specialize function templatesC++ Core Guidelines
- /permissive- (Standards conformance)Microsoft Learn