Nested templates and friends

R

Roger Leigh

I've written a fixed-precision class, "numeric". This stores the
number of decimal places as a template parameter. I've overloaded all
of the normal numerical operators (an example):

[Dp is the number of decimal places in the left-hand operand, and Dpr
the number in the right-hand operand.]

template<unsigned short Dp>
class numeric {
public:
// Round mode and precision are not assigned.
template<unsigned short Dpr>
numeric& operator = (const numeric<Dpr>& rhs);

template<unsigned short Dpr>
numeric& operator += (const numeric<Dpr>& rhs);

template<unsigned short Dpr>
friend numeric operator + <>(const numeric& lhs,
const numeric<Dpr>& rhs);
};

The implementation is like this:

template<unsigned short Dp>
template<unsigned short Dpr>
numeric<Dp>&
numeric<Dp>::eek:perator = (const numeric<Dpr>& rhs)
{ ... }

template<unsigned short Dp>
template<unsigned short Dpr>
numeric<Dp>&
numeric<Dp>::eek:perator += (const numeric<Dpr>& rhs)
{ ... }

template<unsigned short Dp>
template<unsigned short Dpr>
numeric<Dp> operator + (const numeric<Dp>& lhs,
const numeric<Dpr>& rhs)
{ ... }


While the unary operators are fine, I can't get the binary operator, a
friend, to work. I'm sure this is an issue with the template syntax,
but I can't work out what's wrong. I can't add the first template
list to the declaration, since it's within the class declaration, and
removing the <> makes no difference.


Thanks,
Roger
 
R

Roger Leigh

Changing this
template<unsigned short Dpr>
friend numeric operator + <>(const numeric& lhs,
const numeric<Dpr>& rhs);
};

to this

template<unsigned short Dpr>
friend numeric operator + (const numeric& lhs,
const numeric<Dpr>& rhs);

and this
template<unsigned short Dp>
template<unsigned short Dpr>
numeric<Dp> operator + (const numeric<Dp>& lhs,
const numeric<Dpr>& rhs)
{ ... }

to this

template<unsigned short Dp, unsigned short Dpr>
numeric<Dp> operator + (const numeric<Dp>& lhs,
const numeric<Dpr>& rhs)
{ ... }

has solved this problem, though I'm not clear *why* this is the case.
My (hopefully last) remaining problem is a templated copy constructor
and assignment operator:

template<unsigned short Dp>
class numeric {
public:
template<unsigned short Dpr>
numeric (const numeric<Dpr>& rhs);
};

template<unsigned short Dp>
template<unsigned short Dpr>
EPIC::numeric<Dp>::numeric(const EPIC::numeric<Dpr>& rhs):
{ ... }

If I use "template<unsigned short Dp, unsigned short Dpr>" it's still
unhappy:

.../util/numeric.tcc:70: error: prototype for `numeric<Dp>::numeric(const
numeric<Dp>&)' does not match any in class `numeric<Dp>'

How should I declare/define this?


Thanks,
Roger
 
R

Rob Williscroft

Roger Leigh wrote in in
comp.lang.c++:
to this

template<unsigned short Dp, unsigned short Dpr>
numeric<Dp> operator + (const numeric<Dp>& lhs,
const numeric<Dpr>& rhs)
{ ... }

has solved this problem, though I'm not clear *why* this is the case.

My (hopefully last) remaining problem is a templated copy constructor
and assignment operator:

template<unsigned short Dp>
class numeric {
public:
template<unsigned short Dpr>
numeric (const numeric<Dpr>& rhs);
};

template<unsigned short Dp>
template<unsigned short Dpr>
EPIC::numeric<Dp>::numeric(const EPIC::numeric<Dpr>& rhs):
{ ... }

Can you compile this:

#include <iostream>
#include <ostream>

template < unsigned D >
struct num
{
template < unsigned Dr > num( num< Dr > const &rhs );
num() {};
};

template < unsigned D >
template < unsigned Dr >
num< D >::num( num< Dr > const & )
{
std::cout << "template ctor" << std::endl;
}


int main()
{
num< 10 > n10;
num< 12 > n12( n10 );
}


If I use "template<unsigned short Dp, unsigned short Dpr>" it's still
unhappy:

As it should be.
../util/numeric.tcc:70: error: prototype for `numeric<Dp>::numeric>
(const numeric<Dp>&)' does not match any in class `numeric<Dp>'

It maybe a bug in your compiler but surely it should be:
(const numeric said:
How should I declare/define this?

namespace EPIC /* 'EPIC' BTW looks like a macro */
{
template<unsigned short Dp>
template<unsigned short Dpr>
numeric<Dp>::numeric(const numeric<Dpr>& rhs):
{
// ...
}
} /* EPIC:: */

Rob.
 
A

Alberto Barbati

Roger said:
template<unsigned short Dp, unsigned short Dpr>
numeric<Dp> operator + (const numeric<Dp>& lhs,
const numeric<Dpr>& rhs)
{ ... }

Do you realize that your operator+ is not commutative? Expressions like
(a + b) and (b + a) could have a different types and therefore
different values, so this design looks quite error-prone to me.

It would be better to return a numeric<Dpm> where Dpm is the max between
Dp and Dpr. It's a bit tricky as a few compilers (as VC7.1, for example)
chokes about the ?: operator used in a template argument. You may try this:

template <unsigned short N, unsigned short M>
struct static_max
{
static const unsigned short value = (N > M ? N : M);
};

template <unsigned short Dp, unsigned short Dpr>
numeric< static_max<Dp, Dpr>::value >
../util/numeric.tcc:70: error: prototype for `numeric<Dp>::numeric(const
numeric<Dp>&)' does not match any in class `numeric<Dp>'

This error message is suspicious because it should have mentioned
numeric<Dp>::numeric(const numeric<Dpr>&) instead. Did you declare a
copy constructor? Unless you are happy with the implictly generated one,
you should declare it, as a template constructor is never used as a copy
constructor.

Alberto
 
R

Roger Leigh

Do you realize that your operator+ is not commutative? Expressions like
(a + b) and (b + a) could have a different types and therefore
different values, so this design looks quite error-prone to me.

You're right. After thinking about this, I decided on the following
design:

class numeric_base
{
protected:
numeric_base(unsigned short dp): m_dp(dp) {}
private:
unsigned short m_dp;
}

template<unsigned short Dp>
class numeric
{
public:
numeric(): numeric_base(Dp) {}
}

This has the nice property of only needing to define all the operators
in the numeric_base class, which the template then uses. The template
does nothing but give a default m_dp value.
It would be better to return a numeric<Dpm> where Dpm is the max between
Dp and Dpr. It's a bit tricky as a few compilers (as VC7.1, for example)
chokes about the ?: operator used in a template argument. You may try this:

template <unsigned short N, unsigned short M>
struct static_max
{
static const unsigned short value = (N > M ? N : M);
};

template <unsigned short Dp, unsigned short Dpr>
numeric< static_max<Dp, Dpr>::value >
operator+(const numeric<Dp>& lhs, const numeric<Dpr> rhs)
{...}

That looks quite clever, but I like to keep template magic to the
minimum, so that both I and those I work with can understand it!
This error message is suspicious because it should have mentioned
numeric<Dp>::numeric(const numeric<Dpr>&) instead. Did you declare a
copy constructor? Unless you are happy with the implictly generated one,
you should declare it, as a template constructor is never used as a copy
constructor.

Ah, I didn't realise that. So I need both a template constructor and a
copy constructor.


Thanks,
Roger
 
R

Roger Leigh

Can you compile this:

#include <iostream>
#include <ostream>

template < unsigned D >
struct num
{
template < unsigned Dr > num( num< Dr > const &rhs );
num() {};
};

template < unsigned D >
template < unsigned Dr >
num< D >::num( num< Dr > const & )
{
std::cout << "template ctor" << std::endl;
}

This works just fine (with GCC 3.3.3). However, I've changed the design
to remove the need for the template complexity.

Could anyone recommend any book or online documentation about the rules
for template syntax? I find it quite confusing. The books I have only
go into the basic usage of existing templates, and leave all this stuff
untouched.


One last template question:

If a library defines a templated function:

template<typename T>
do_foo(const T& object)
{ }

and the user is required to specalise it for their type:

class mytype;

template<>
do_foo(const mytype& object)
{ }

how should one specialise for a templated type?

template<typename T>
class mytype;

template<????>
do_foo(const mytype<??>& object)
{ }

I've tried quite a few things, like
template<> template<typename T>
but the compiler wasn't happy with any of them. Is this possible to do,
or does it require specialisation for every mytype<T> I use?


Thanks,
Roger
 
R

Rob Williscroft

Roger Leigh wrote in in comp.lang.c++:
If a library defines a templated function:

template<typename T>
do_foo(const T& object)
{ }

No return type.
and the user is required to specalise it for their type:

class mytype;

template<>
do_foo(const mytype& object)
{ }

how should one specialise for a templated type?

template<typename T>
class mytype;

template<????>
do_foo(const mytype<??>& object)
{ }

I've tried quite a few things, like
template<> template<typename T>
but the compiler wasn't happy with any of them. Is this possible to do,
or does it require specialisation for every mytype<T> I use?

#include <iostream>
#include <ostream>

template<typename T>
void do_foo( T const & )
{
std::cout << "do_foo< T >( T const & )" << std::endl;
}


class mytype
{
};

template<>
void do_foo( mytype const & )
{
std::cout << "do_foo< mytype >( mytype const & )" << std::endl;
}



template < typename T >
class myclass_template
{
};

template < typename T >
void do_foo( myclass_template< T > const & )
{
std::cout << "do_foo< T >( myclass_template< T > const & )" << std::endl;
}

/* The above is an *overload* of do_foo< T >( T const & ), it differs
only in argument type. The previous function do_foo< mytype > is a
specialization.
*/


int main()
{
do_foo( 0 );

mytype a;
do_foo( a );

myclass_template< int > b;
do_foo( b );
}


Rob.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top