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 f
Likewise, 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-called
template
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 in
template
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;