Re: why should the function defined in class
On Mar 10, 2:44 pm, hpsoar <hps...@gmail.com> wrote:
Item 46: Define non-member functions inside templates when type
conversions are desired
Just a note, but it would help if you'd have cited the
reference, so we'd know what book to look up Item 46 in.
Item 24 explains why only non-member functions are eligible
for implicit type conversions on all arguments, and it uses as
an example the operator* function for a Rational class. I
recommend you familiarize yourself with that example before
continuing, because this Item extends the discussion with a
seemingly innocuous modification to Item 24's example: it
templatizes both Rational and operator*:
template<typename T>
class Rational {
public:
Rational(const T& numerator = 0, // see Item 20 for why params
const T& denominator = 1); // are now passed by reference
const T numerator() const; // see Item 28 for why return
const T denominator() const; // values are still passed by valu=
e,
... // Item 3 for why they're const
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs,
const Rational<T>& rhs)
{ ... }
As in Item 24, we want to support mixed-mode arithmetic, so we
want the code below to compile. We expect that it will,
because we're using the same code that works in Item 24. The
only difference is that Rational and operator* are now
templates:
Rational<int> oneHalf(1, 2); // this example is from Item 24,
// except Rational is now a templat=
e
Rational<int> result = oneHalf * 2; // error! won't compile
[explination of difference between template type deduction
and operator overload resolution deleted...]
template<typename T>
class Rational {
public:
...
friend // declare operator*
const Rational operator*(const Rational& lhs, // function (see
const Rational& rhs); // below for details)
};
this is a example in Effectvie C++ 3e, it is said that the
function declared as a friend should also be defined in class
Rational.
Not only should be, but almost must be. There's no way of
defining it later.
It proves to be true. But I really didn't understand the
explanation :If we declare a function ourselves (which is what
we're doing inside the Rational template), we're also
responsible for defining that function. Can anyone explain it
more clear?
The difference is simple. The friend function, above, is NOT a
template. Each instantiation of Rational declares a different
non-template friend, e.g.:
Rational< int > const operator*( Rational< int > const&,
Rational< int > const& ) ;
Rational< double > const operator*( Rational< double > const&,
Rational< double > const& ) ;
etc. So:
1. You need a separate implementation for each of these
functions. If you provide it in the class template, the
compiler will generate it automatically each time it is
needed. Otherwise, you have to write it, for each
instantiation of the template (which, of course, isn't
really feasable, since you don't know what types will be
used to instantiate the template).
2. Since it isn't a template, type deduction doesn't apply;
only operator overload resolution (which allows implicit
conversions).
3. The compiler finds the operator by ADL; that is: one of the
arguments must be a Rational<T> (for some T), in which case,
the compiler looks in Rational<T>, class T (if T is a class
type) and any namespaces which contain Rational or T (if T
is a user defined type). Note that it does NOT look in
Rational; as far as the compiler is concerned, at this
point, Rational doesn't exist, only instantiations of
Rational.
The usual way of doing this is to define a member *=, and use a
templated base class to provide the binary operators, e.g.:
template< typename DerivedType >
struct ArithmeticOperators
{
// ...
friend DerivedType operator*(
DerivedType const& lhs,
DerivedType const& rhs )
{
DerivedType result( lhs ) ;
result *= rhs ;
return result ;
}
// No actual members...
} ;
, then
template< typename T >
class Rational : public ArithmeticOperators< Rational< T > >
{
// ...
Rational& operator*=( Rational const& other ) ;
// ...
} ;
The <op>= operators are normally members, so you don't have to
worry about defining them inline.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34