C艹11 Language Extensions – Templates
Extern templates
A template specialization can be explicitly declared as a way to suppress multiple instantiations. For example:
#include "MyVector.h"
extern template class MyVector<int>; // Suppresses implicit instantiation below --
// MyVector<int> will be explicitly instantiated elsewhere
void foo(MyVector<int>& v)
{
// use the vector in here
}
The “elsewhere” might look something like this:
#include "MyVector.h"
template class MyVector<int>; // Make MyVector available to clients (e.g., of the shared library
This is basically a way of avoiding significant redundant work by the compiler and linker.
See:
- Standard 14.7.2 Explicit instantiation
- [N1448==03-0031] Mat Marcus and Gabriel Dos Reis: Controlling Implicit Template Instantiation.
Template aliases
How can we make a template that’s “just like another template” but possibly with a couple of template arguments specified (bound)? Consider:
template<class T>
using Vec = std::vector<T,My_alloc<T>>; // standard vector using my allocator
Vec<int> fib = { 1, 2, 3, 5, 8, 13 }; // allocates elements using My_alloc
vector<int,My_alloc<int>> verbose = fib; // verbose and fib are of the same type
The keyword using
is used to get a linear notation “name followed by what it refers to.” We tried with the conventional and convoluted typedef
solution, but never managed to get a complete and coherent solution until we settled on a less obscure syntax.
Specialization works (you can alias a set of specializations but you cannot specialize an alias) For example:
template<int>
struct int_exact_traits { // idea: int_exact_trait<N>::type is a type with exactly N bits
typedef int type;
};
template<>
struct int_exact_traits<8> {
typedef char type;
};
template<>
struct int_exact_traits<16> {
typedef char[2] type;
};
// ...
template<int N>
using int_exact = typename int_exact_traits<N>::type; // define alias for convenient notation
int_exact<8> a = 7; // int_exact<8> is an int with 8 bits
In addition to being important in connection with templates, type aliases can also be used as a different (and IMO better) syntax for ordinary type aliases:
typedef void (*PFD)(double); // C style
using PF = void (*)(double); // using plus C-style type
using P = auto (*)(double)->void; // using plus suffix return type
See also:
- the C艹 draft: 14.6.7 Template aliases; 7.1.3 The typedef specifier
- [N1489=03-0072] Bjarne Stroustrup and Gabriel Dos Reis: Templates aliases for C艹.
- [N2258=07-0118] Gabriel Dos Reis and Bjarne Stroustrup: Templates Aliases (Revision 3) (final proposal).
Variadic templates
Problems to be solved:
- How to construct a class with 1, 2, 3, 4, 5, 6, 7, 8, 9, or … initializers?
- How to avoid constructing an object out of parts and then copying the result?
- How to construct a
tuple
?
The last question is the key: Think tuple
! If you can make and access general tuples the rest will follow.
Here is an example (from “A brief introduction to Variadic templates” (see references)) implementing a general, type-safe, printf()
. It would probably be better to use boost::format
, but consider:
const string pi = "pi";
const char* m = "The value of %s is about %g (unless you live in %s).\n";
printf(m, pi, 3.14159, "Indiana");
The simplest case of printf()
is when there are no arguments except the format string, so we’ll handle that first:
void printf(const char* s)
{
while (s && *s) {
if (*s=='%' && *++s!='%') // make sure that there wasn't meant to be more arguments
// %% represents plain % in a format string
throw runtime_error("invalid format: missing arguments");
std::cout << *s++;
}
}
That done, we must handle printf()
with more arguments:
template<typename T, typename... Args> // note the "..."
void printf(const char* s, T value, Args... args) // note the "..."
{
while (s && *s) {
if (*s=='%' && *++s!='%') { // a format specifier (ignore which one it is)
std::cout << value; // use first non-format argument
return printf(++s, args...); // "peel off" first argument
}
std::cout << *s++;
}
throw std::runtime error("extra arguments provided to printf");
}
This code simply “peels off” the first non-format argument and then calls itself recursively. When there are no more non-format arguments, it calls the first (simpler) printf()
(above). This is rather standard functional programming done at compile time. Note how the overloading of <<
replaces the use of the (possibly erroneous) “hint” in the format specifier.
The Args...
defines what is called a “parameter pack.” That’s basically a sequence of (type/value) pairs from which you can “peel off” arguments starting with the first. When printf()
is called with one argument, the first printf(const char*)
is chosen. When printf()
is called with two or more arguments, the second printf(const char*, T value, Args... args))
is chosen, with the first argument as s
, the second as value
, and the rest (if any) bundled into the parameter pack args
for later use. In the call
printf(++s, args...);
the parameter pack args
is expanded so that the next argument can now be selected as value. This carries on until args
is empty (so that the first printf()
is called).
If you are familiar with functional programming, you should find this an unusual notation for a pretty standard technique. If not, here are some small technical examples that might help. First we can declare and use a simple variadic template function (just like printf()
above):
template<class ... Types>
void f(Types ... args); // variadic template function
// (i.e. a function that can take an arbitrary number of arguments of arbitrary types)
f(); // OK: args contains no arguments
f(1); // OK: args contains one argument: int
f(2, 1.0); // OK: args contains two arguments: int and double
We can build a variadic type:
template<typename Head, typename... Tail>
class tuple<Head, Tail...>
: private tuple<Tail...> { // here is the recursion
// Basically, a tuple stores its head (first (type/value) pair
// and derives from the tuple of its tail (the rest of the (type/value) pairs.
// Note that the type is encoded in the type, not stored as data
typedef tuple<Tail...> inherited;
public:
tuple() { } // default: the empty tuple
// Construct tuple from separate arguments:
tuple(typename add_const_reference<Head>::type v, typename add_const_reference<Tail>::type... vtail)
: m_head(v), inherited(vtail...) { }
// Construct tuple from another tuple:
template<typename... VValues>
tuple(const tuple<VValues...>& other)
: m_head(other.head()), inherited(other.tail()) { }
template<typename... VValues>
tuple& operator=(const tuple<VValues...>& other) // assignment
{
m_head = other.head();
tail() = other.tail();
return *this;
}
typename add_reference<Head>::type head() { return m_head; }
typename add_reference<const Head>::type head() const { return m_head; }
inherited& tail() { return *this; }
const inherited& tail() const { return *this; }
protected:
Head m_head;
}
Given that definition, we can make tuples (and copy and manipulate them):
tuple<string,vector,double> tt("hello",{1,2,3,4},1.2);
string h = tt.head(); // "hello"
tuple<vector<int>,double> t2 = tt.tail(); // {{1,2,3,4},1.2};
It can get a bit tedious to mention all of those types, so often, we deduce them from argument types, e.g. using the standard library make_tuple()
:
template<class... Types>
tuple<Types...> make_tuple(Types&&... t) // this definition is somewhat simplified (see standard 20.5.2.2)
{
return tuple<Types...>(t...);
}
string s = "Hello";
vector<int> v = {1,22,3,4,5};
auto x = make_tuple(s,v,1.2);
See also:
- Standard 14.6.3 Variadic templates
- [N2151==07-0011] D. Gregor, J. Jarvi: Variadic Templates for the C艹0x Standard Library.
- [N2080==06-0150] D. Gregor, J. Jarvi, G. Powell: Variadic Templates (Revision 3).
- [N2087==06-0157] Douglas Gregor: A Brief Introduction to Variadic Templates.
- [N2772==08-0282] L. Joly, R. Klarer: Variadic functions: Variadic templates or initializer lists? – Revision 1.
- [N2551==08-0061] Sylvain Pion: A Variadic
std::min(T, ...)
for the C艹 Standard Library (Revision 2). - Anthony Williams: An Introduction to Variadic Templates in C艹0x. DevX.com, May 2009.
Local types as template arguments
In C艹98, local and unnamed types could not be used as template arguments. This could be a burden, so C艹11 lifts the restriction:
void f(vector<X>& v)
{
struct Less {
bool operator()(const X& a, const X& b) { return a.v<b.v; }
};
sort(v.begin(), v.end(), Less()); // C艹98: error: Less is local
// C艹11: ok
}
In C艹11, we also have the alternative of using a lambda expression:
void f(vector<X>& v)
{
sort(v.begin(), v.end(),
[] (const X& a, const X& b) { return a.v<b.v; }); // C艹11
}
It is worth remembering that naming action can be quite useful for documentation and an encouragement to good design. Also, non-local (necessarily named) entities can be reused.
C艹11 also allows values of unnamed types to be used as template arguments:
template<typename T> void foo(T const& t){}
enum X { x };
enum { y };
int main()
{
foo(x); // C艹98: ok; C艹11: ok
foo(y); // C艹98: error; C艹11: ok
enum Z { z };
foo(z); // C艹98: error; C艹11: ok
}
See also:
- [N2402=07-0262] Anthony Williams: Names, Linkage, and Templates (rev 2).
- [N2657] John Spicer: Local and Unnamed Types as Template Arguments.