f<>(/*...*/)
T&&
is an rvalue reference.
T&&
for some template argument T
is a forwarding reference.
[Example:
struct T{}; void foo(T&& t); // rvalue reference template <typename T> void bar(T&& t); // forwarding reference int main() { T t{}; foo(t); // error: cannot bind rvalue ref to t bar(t); // OK: reference collapse to T& }-- end example]
For a function this simply involves providing a definition in one of the TUs.
This is in general infeasible for a function template as a specialization or explicit instantiation must be provided for every instantiation that might be invoked.
(Additionally, such explicit specializations must be declared in every translation unit, although absence may not be diagnosed by all implementations.)
This can be a problem as code changes: you might for example start with a function in an anonymous namespace, used in
a single .cpp file.
Then you want to use it elsewhere, so you move the definition into a public namespace, and put
the declaration in a header so you can use it in a second .cpp file.
inline
).
A function template can be defined in multiple translation units, and the linker will pick one copy of each actual instantiation.
[Example:
inline void foo() { static object theObject; // only one instance } template <typename T> void foo() { static object theObjects; // one instance per T }-- end example]
A (non-inlined) function has a single instantiation in a program; multiple instantiations of a (non-inlined) function template increase the code size.
[Example:
template<class T> auto f(const T&) { struct A {}; return A{}; } void g(short s) { auto a=f(s); a=f(s+1); // error -- type mismatch }-- end example]
[Example:
struct X{}; struct Y : X {}; void foo(X, X); template<typename T> void bar(T, T); int main() { X x; Y y; foo(x, y); // OK bar(x, y); // error }-- end example]
or a different case:
[Example:
struct X{X(int) {} }; bool operator==(X, X); bool zero(X x) {return x == 0;} // OK template<typename T> struct Y { Y(T) {} }; template<class T> bool operator==(Y<T>, Y<T>); // non-member allows conversions, right? bool zero(Y<int> y) {return y == 0;} // error: deduction failed-- end example]
initializer_list
.
[Example:
#include <initializer_list> void f(std::initializer_list<int> i) {} template <typename T> void ft(T t) {} int main() { f({1, 3, 5}); ft({1, 3, 5}); // error: can't deduce initializer_list }-- end example]
auto f() { auto x = [](int){}; auto y = [](auto){}; auto z = +y; // error: no conversion found return +x; // OK }-- end example]
This makes using function pointers with function templates trickier than with functions as the instantiation required must be explicitly specified.
This can be done by giving the desired template arguments for the specialization desired.
When the template arguments are deducable an alternative is using a pointer to a function with the desired argument types to select the correct instantiation.
[Example:
struct X {}; void foo(X, X); X make_foo(); template<typename T> void bar(T, T); template<typename T> T make_bar(T, T); int main() { auto foo_ptr = &foo; // OK auto make_foo_ptr = &make_foo; // OK auto bar_ptr = &bar; // error auto make_bar_ptr = &make_bar; // error auto bar_ptr1 = &bar<X>; // OK auto make_bar_ptr1 = &make_bar<X>; // OK void (*bar_ptr2)(X, X) = &bar; // OK, template arguments deduced }-- end example]
[Example:
#include <iostream> template<class T> T f_template(const T &arg){ return arg; } int f_int(const int &arg){ return arg; } auto f_lambda = [](const int &arg){ return arg; }; auto f_glambda = [](const auto &arg){ return arg; } template<class F, class T> auto call_me(F f, const T&arg){ return f(arg); } int main(int, char**){ std::cout << call_me(f_int, 42) << "\n"; // works std::cout << call_me(f_lambda, 42) << "\n"; // works std::cout << call_me(f_glambda, 42) << "\n"; // generic lambda works std::cout << call_me(f_template, 42) << "\n"; // template doesn't work }-- end example]
[Example:
struct A { A() {throw 0;} void* operator new(std::size_t,int,short); template<class T> void operator delete(void*,T,T); }; void f() {new (0,0) A;} // no deallocation function called-- end example]
[Example:
template<class T> void f(T*); // #1 template<class T> void f(T**); // #2 void g(int*); // g ~ f<int> void g(int**); // and yet... void h() { f<int>(nullptr); // OK: calls #2 g(nullptr); // error: ambiguous }-- end example]
[Example:
template<class T> void f(const T&); // #1 template<> void f(const int&); // #2: specializes #1 void f(volatile int&); // #3 void g(int i) {f(i);} // calls #3-- end example]
If we added one (more) template parameter to each of the three declarations of f, the call would become ambiguous.
f<>(/*...*/)
[Example:
void foo(int); template<typename T> void foo(T); int main() { foo(1); // Selects function (best match) foo<>(1); // Selects function template }-- end example]
[Example:
template<typename T> void bar(T); template<> void bar(double) {} int main() { bar(1.0); }-- end example]
For a function template you can specify an instantation explicitly by providing the template arguments.
[Example:
template<typename T> void bar(T); int main() { bar<int>(1.0); }-- end example]
[Example: (from [class.copy.ctor] p5)
struct S { template<typename T> S(T); S(); }; S g; void h() { S a(g); // does not instantiate the member template to produce S::S<S>(S); // uses the implicitly declared copy constructor }-- end example]
A function template has two phases of compilation, once when the definition is reached and once when the function is instantiated.
This can mean there is no guarantee of validity checking until instantiation.
[Example:
template<class T> bool equal(const Container<T> &c,const T *a) { for(int i=0;i<c.size();++i) if(c[i]!=a) return false; return true; }-- end example]
The expression c[i]!=a
is comparing a T
against a T*
and will (almost certainly) fail at instantiation.
(And even it instantiates, it's unlikely to be the correct semantics!)
static_assert
triggers at definition time.
static_assert
triggers at instantation time.
[Example:
void foo(int i) { static_assert(sizeof(i) == 88); // asserts: (typo for '8') } template<typename T> void bar(T t) { static_assert(sizeof(int) == 88); // ill-formed, no diagnostic required (non-dependent) static_assert(sizeof(T) == 88); // asserts when instantiated (dependent) }-- end example]
[Example:
struct X {}; void foo(X arg = 42); // error: even if default argument never used template<typename T> void bar(T arg = 42); // OK int main() { X x; bar(x); // OK bar<X>(); // error: only if default argument used }-- end example]
[Example:
// Taken from [temp.res] p10, slightly simplified void f(char); enum E { e }; template<class T> void g(T t) { f(1); // f(char) f(t); // dependent } void f(E); void h() { g(e); // will cause one call of f(char) followed by one call of f(E) }-- end example]
The equivalent case for a function has different behaviour:
[Example:
void f(char); enum E { e }; void g(E t) { f(1); // f(char) f(t); // f(char) ! } void f(E); void h() { g(e); // will cause two calls of f(char) }-- end example]
typename
and template
Note that while P0634R3 (applied to the working paper in JAX '18) reduces the number of places where typename
is required, it does not eliminate them all.
For example, that paper gives an example where omitting typename
changes the meaning of the code:
template<typename T> void f() { void (*pf)(T::X); // Variable pf of type void* initialized with T::X };If
typename
is added:
template<typename T> void f() { void (*pf)(typename T::X); // Variable pf of type pointer to function taking T::X and returning void. };
There are also cases where template
must be used to avoid parsing ambiguity with the less than operator.
[Example:
// Taken from [temp.names] p3, slightly simplified struct X { template<std::size_t> X* alloc(); template<std::size_t> static X* adjust(); }; template<class T> void f(T* p) { T* p1 = p->alloc<200>(); // ill-formed: < means less than T* p2 = p->template alloc<200>(); // OK: < starts template argument list }-- end example]
With the "Constrained auto" syntax adopted in P1141:
[Example:
// library code template<class T> concept Container=/*...*/; struct Widget {}; struct Button : Widget {}; // user code void f(Container auto&) {} template<> void f(std::vector<int>&) {} // OK void g(Widget&) {} template<> void g(Button&) {} // error-- end example]
With the abbreviated syntax from the Concepts TS the difference between f
and g
becomes less visible:
[Example:
// user code void f(Container&) {} template<> void f(std::vector<int>&) {} // OK void g(Widget&) {} template<> void g(Button&) {} // error-- end example]