Without understanding the likes of the previous examples in this section, novices find themselves frustrated because they can’t get a simple stream output inserter to work. If you don’t define your operators inside the definition of Box
, you must provide the forward declarations we showed earlier:.//: C05:Box1.cpp
// Defines template operators
#include
using namespace std;
// Forward declarations
template
class Box;
template
Box
template
ostream& operator<<(ostream&, const Box
template
class Box {
T t;
public:
Box(const T& theT) : t(theT) {}
friend Box operator+<>(const Box
friend ostream& operator<< <>(ostream&, const Box
};
template
Box
return Box
}
template
ostream& operator<<(ostream& os, const Box
return os << '[' << b.t << ']';
}
int main() {
Box
cout << b1 + b2 << endl; // [3]
// cout << b1 + 2 << endl; // no implicit conversions!
} ///:~
Here we are defining both an addition operator and an output stream operator. The main program reveals a disadvantage of this approach: you can’t depend on implicit conversions (see the expression b1 + 2
) because templates do not provide them. Using the in-class, non-template approach is shorter and more robust:.//: C05:Box2.cpp
// Defines non-template operators
#include
using namespace std;
template
class Box {
T t;
public:
Box(const T& theT) : t(theT) {}
friend Box operator+(const Box
const Box
return Box
}
friend ostream& operator<<(ostream& os,
const Box
return os << '[' << b.t << ']';
}
};
int main() {
Box
cout << b1 + b2 << endl; // [3]
cout << b1 + 2 << endl; // [3]
} ///:~
Because the operators are normal functions (overloaded for each specialization of Box
—just int in this case, of course), implicit conversions are applied as normal; so the expression b1 + 2 is valid.Friend templates
You can be precise as to which specializations of a template are friends of a class. In the examples in the previous section, only the specialization of the function template f
with the same type that specialized Friendly was a friend. For example, only the specialization f// Inside Friendly:
friend void f<>(const Friendly
By using double
instead of T, the double specialization of f has access to the non-public members of any Friendly specialization. The specialization fLikewise, if you were to declare a non-template function with no parameters dependent on T
, that single function would be a friend to all instances of Friendly:.// Inside of Friendly:
friend void g(int); // g(int) befriends all Friendly’s
As always, since g(int)
is unqualified, it must be defined at file scope (the namespace scope containing Friendly).It is also possible to arrange for all specializations of f
to be friends for all specializations of Friendly, with a so-calledtemplate
class Friendly {
template
Since the template argument for the friend declaration is independent of T
, any combination of T and U is allowed, achieving the friendship objective. Like member templates, friend templates can appear within non-template classes as well.Template programming idioms
Since language is a tool of thought, new language features tend to spawn new programming techniques. In this section we cover some commonly-used template programming idioms that have emerged in the years since templates were added to the C++ language.[62]
Traits
The traits template technique, pioneered by Nathan Myers, is a means of bundling type-dependent declarations together. In essence, using traits allows you to "mix and match" certain types and values with contexts that use them in a flexible manner, while keeping your code readable and maintainable.
The simplest example of a traits template is the numeric_limits
class template defined intemplate
public:
static const bool is_specialized = false;
static T min() throw();
static T max() throw();
static const int digits = 0;
static const int digits10 = 0;
static const bool is_signed = false;
static const bool is_integer = false;
static const bool is_exact = false;
static const int radix = 0;