};
int main() {
Box<int> b1(1), b2(2);
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<int>(const Friendly<int>&) is a friend of the class Friendly<int>. This was accomplished by using the template parameter for Friendly to specialize f in its friend declaration. If we had wanted to, we could have made a particular, fixed specialization of f a friend to all instances of Friendly, like this:.
// Inside Friendly:
friend void f<>(const Friendly<double>&);
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<double>( ) still isn’t instantiated unless it is explicitly called, of course.
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 friend template, as follows:
template<class T>
class Friendly {
template<class U> friend void f<>(const Friendly<U>&);
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 <limits>. The primary template is defined as follows:
template<class T> class numeric_limits {
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;
static T epsilon() throw();
static T round_error() throw();
static const int min_exponent = 0;
static const int min_exponent10 = 0;
static const int max_exponent = 0;
static const int max_exponent10 = 0;
static const bool has_infinity = false;
static const bool has_quiet_NaN = false;
static const bool has_signaling_NaN = false;
static const float_denorm_style has_denorm =
denorm_absent;
static const bool has_denorm_loss = false;
static T infinity() throw();
static T quiet_NaN() throw();
static T signaling_NaN() throw();
static T denorm_min() throw();
static const bool is_iec559 = false;
static const bool is_bounded = false;
static const bool is_modulo = false;
static const bool traps = false;
static const bool tinyness_before = false;
static const float_round_style round_style =
round_toward_zero;
};
The <limits> header defines specializations for all fundamental, numeric types (in which case the member is_specialized is set to true). To obtain the base for the double version of your floating-point number system, for example, you can use the expression numeric_limits<double>::radix. To find the smallest integer value available, you can use numeric_limits<int>::min( ). Not all members of numeric_limits apply to all fundamental types, of course. (For example, epsilon( ) is only for floating-point types.).
The values that will always be integral are static data members of numeric_limits; those that may not be integral, such as the minimum value for float, are implemented as static inline member functions. This is because C++ allows only integral static data member constants to be initialized inside a class definition. Other members, such as floating-point values, must be initialized at file scope outside the class definition, which is not appropriate in a header file. Since the needed value in that case will be placed in an implementation (.cpp) file, the value will not be available for compile-time optimization. Inline member functions of a class template, on the other hand, can be included in a header file, and thus facilitate compile-time optimization.
In Chapter 3 you saw how traits are used to control the character-processing functionality used by the string classes. The classes std::string and std::wstring are specializations of the std::basic_string template, which is defined as follows:
template<class charT,
class traits = char_traits<charT>,
class allocator = allocator<charT> >
class basic_string;
The template parameter charT represents the underlying character type, which is usually either char or wchar_t. The primary char_traits template is typically empty, and specializations for char and wchar_t are provided by the standard library. Here is the specification of the specialization char_traits<char> according to the C++ standard:.