Ru-Brd |
A duo is the assembly of two objects into a single type. This is similar to the std::pair class template in the standard library, but because we will add slightly different functionality to this very basic utility, we opted for a name other than pair to avoid confusion with the standard item. At its very simplest, we can define Duo as follows : template <typename T1, typename T2> struct Duo { T1 v1; // value of first field T2 v2; // value of second field }; This can, for example, be useful as a return type for a function that may return an invalid result: Duo<bool,X> result = foo(); if (result.v1) { // result is valid; value is in result.v2 } Many other applications are possible. The benefit of Duo as defined here is not insignificant, but it is rather small. After all, it would not be that much work to define a structure with two fields, and doing so allows us to choose meaningful names for these fields. However, we can extend the basic facility in a few ways to add to the convenience. First, we can add constructors: template <typename T1, typename T2> class Duo { public: T1 v1; // value of first field T2 v2; // value of second field // constructors Duo() : v1(), v2() { } Duo (T1 const& a, T2 const& b) : v1(a), v2(b) { } }; Note that we used an initializer list for the default constructor so that the members get zero initialized for built-in types (see Section 5.5 on page 56). To avoid the need for explicit type parameters, we can further add a function so that the field types can be deduced : template <typename T1, typename T2> inline Duo<T1,T2> make_duo (T1 const& a, T2 const& b) { return Duo<T1,T2>(a,b); } Now the creation and initialization of a Duo becomes more convenient . Instead of Duo<bool,int> result; result.v1 = true; result.v2 = 42; return result; we can write return make_duo(true,42); Good C++ compilers can optimize this well enough so that this generates code equivalent to return Duo<bool,int>(true,42); Another refinement is to provide access to the field types, so that adapter templates can be built on top of Duo : template <typename T1, typename T2> class Duo { public: typedef T1 Type1; // type of first field typedef T2 Type2; // type of second field enum { N = 2 }; // number of fields T1 v1; // value of first field T2 v2; // value of second field // constructors Duo() : v1(), v2() { } Duo (T1 const& a, T2 const& b) : v1(a), v2(b) { } }; At this stage we're rather close to the implementation of std::pair with the following differences:
A more powerful and cleaner implementation might looks as follows: // tuples/duo1.hpp #ifndef DUO_HPP #define DUO_HPP template <typename T1, typename T2> class Duo { public: typedef T1 Type1; // type of first field typedef T2 Type2; // type of second field enum { N = 2 }; // number of fields private: T1 value1; // value of first field T2 value2; // value of second field public: // constructors Duo() : value1(), value2() { } Duo (T1 const & a, T2 const & b) : value1(a), value2(b) { } // for implicit type conversion during construction template <typename U1, typename U2> Duo (Duo<U1,U2> const & d) : value1(d.v1()), value2(d.v2()) { } // for implicit type conversion during assignments template <typename U1, typename U2> Duo<T1, T2>& operator = (Duo<U1,U2> const & d) { value1 = d.value1; value2 = d.value2; return *this; } // field access T1& v1() { return value1; } T1 const& v1() const { return value1; } T2& v2() { return value2; } T2 const& v2() const { return value2; } }; // comparison operators (allow mixed types): template <typename T1, typename T2, typename U1, typename U2> inline bool operator == (Duo<T1,T2> const& d1, Duo<U1,U2> const& d2) { return d1.v1()==d2.v1() && d1.v2()==d2.v2(); } template <typename T1, typename T2, typename U1, typename U2> inline bool operator != (Duo<T1,T2> const& d1, Duo<U1,U2> const& d2) { return !(d1==d2); } // convenience function for creation and initialization template <typename T1, typename T2> inline Duo<T1,T2> make_duo (T1 const & a, T2 const & b) { return Duo<T1,T2>(a,b); } #endif // DUO_HPP We made the following changes:
All the member templates are used to enable mixed type operations. That is, we can initialize, assign, and compare a Duo for which an implicit type conversion is necessary to perform the task. For example: // tuples/duo1.cpp #include "duo1.hpp" Duo<float,int> foo () { return make_duo(42,42); } int main() { if (foo() == make_duo(42,42.0)) { } } In this program, in foo() there is a conversion from the return type of make_duo() , Duo<int,int> to the return type of foo() , Duo<float,int> . Similarly, the return value of foo() is compared with the return value of make_duo(42, 42.0) , which is a Duo<int,double> . It would not be difficult to add Trio and other templates to collect larger numbers of values. However, a more structured alternative can be obtained by nesting Duo objects. This idea is developed in the following sections. |
Ru-Brd |