Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

template function specialization troubles

0 views
Skip to first unread message

Alan Lehotsky

unread,
Sep 17, 2004, 6:49:35 AM9/17/04
to
I'm trying to write a templated function with the following behavior.

Given a type T and an integer width, write a function that creates a
constant of either type T (for POD T) or of type T::basetype for any
aggregate type having a T::basetype that's a POD. The constant is
composed of a bitstring of length 'width'.

The following works as long as I don't try to handle aggregate types.
But SFINAE doesn't stop gcc (2.95.3 or 3.4.2) from complaining about
the specialization

template<> inline T::basetype MASK<T>(int width) ....

producing about 80 lines of errors all told. I've elided some that I'm
pretty sure are irrelevant. How can I make the return type of the
function do the right thing here?

=================================================
struct composite {
int i;
typedef int basetype;
};

template <class T> inline T MASK (int width) {
if (width < 0 || width > (sizeof(T)*8))
return T (0);
else if (width == (sizeof(T)*8))
return ~T (0);
else
return T(~((~T(0)) << width));
}

// Specialization for composite data types
#ifdef BAD
template <> inline T::basetype
MASK<T>(int width)
{
return MASK<typename T::basetype>(width);
}
#endif

#include <iostream>

int main ()
{
int svar = -3;
std::cout << "MASK<typeof(t)>(5) = " << (int) MASK<typeof (svar)>(5)
<< std::endl;
std::cout << "MASK<char>(3) = " << (int) MASK<char>(3) << std::endl;
std::cout << "MASK<unsigned char>(4) = " << (int) MASK<unsigned
char>(4) << std::endl;
std::cout << "MASK<long long>(70) = " << MASK<long long>(70) <<
std::endl;
std::cout << "MASK<long long>(64) = " << MASK<long long>(64) <<
std::endl;
#ifdef BAD
std::cout << "MASK<composite>(7) = " << MASK<composite>(7) <<
std::endl;
#endif
return 0;
}
=============================================

/tools/linux/gcc-3.4.2/bin/g++ -o mask mask.cxx -DBAD
mask.cxx:18: error: `T' has not been declared
mask.cxx:19: error: expected init-declarator before "MASK"
mask.cxx:19: error: expected `;' before "MASK"
mask.cxx: In function `int main()':
.........
mask.cxx: In function `T MASK(int) [with T = composite]':
mask.cxx:36: instantiated from here
mask.cxx:9: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
mask.cxx:11: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
mask.cxx:13: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
[apl]aluminum$

--
--
Alan Lehotsky <a...@alum.mit.edu>

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Rob Williscroft

unread,
Sep 18, 2004, 6:59:09 AM9/18/04
to
Alan Lehotsky wrote in news:apl-039B6A.21201416092004
@news6.east.earthlink.net in comp.lang.c++.moderated:

> struct composite {
> int i;
> typedef int basetype;
> };
>
> template <class T> inline T MASK (int width) {
> if (width < 0 || width > (sizeof(T)*8))
> return T (0);
> else if (width == (sizeof(T)*8))
> return ~T (0);
> else
> return T(~((~T(0)) << width));
> }
>
> // Specialization for composite data types
> #ifdef BAD
> template <> inline T::basetype
> MASK<T>(int width)
> {
> return MASK<typename T::basetype>(width);
> }
> #endif

template < typename T >
inline typename T::basetype
MASK(int width, typename T::basetype * = 0 )


{
return MASK<typename T::basetype>(width);
}

The extra argument (typename T::basetype * = 0) *isn't* required
for a conforming compiler, but it helps with gcc 3.2, sorry
I don't have 2.95 so I can't check.

The *typename* is required by a conforming compiler, but again
I havent gcc 2.95 so you may need to #ifdef it.

HTH.

Rob.
--
http://www.victim-prime.dsl.pipex.com/

Alberto Barbati

unread,
Sep 18, 2004, 7:12:06 AM9/18/04
to
Alan Lehotsky wrote:

>
> template <class T> inline T MASK (int width) {
> if (width < 0 || width > (sizeof(T)*8))
> return T (0);
> else if (width == (sizeof(T)*8))
> return ~T (0);
> else
> return T(~((~T(0)) << width));
> }
>
> // Specialization for composite data types
> #ifdef BAD
> template <> inline T::basetype
> MASK<T>(int width)
> {
> return MASK<typename T::basetype>(width);
> }
> #endif
>

This can't compile for at least three reasons:

1) the specialization syntax is incorrect, there's no <T> after MASK
2) if you fully specialize the template you can no longer refer to T
3) you missed the typename keyword before the first T::basetype

Syntax apart, yours cannot be a template specialization because you want
to replace T with T::basetype in the signature. A specialization can
only replace T with a "real" type (such as int, float, MyClass, etc.)
that doesn't depend on T.

> #include <iostream>
>
> int main ()
> {
> int svar = -3;
> std::cout << "MASK<typeof(t)>(5) = " << (int) MASK<typeof (svar)>(5)
> << std::endl;

What's "typeof"? Please stick to standard C++... ;-)
BTW, using std::endl is overkill, as it will flush the buffer. Usually
writing a "\n" is more appropriate.

What I suggest is the following. Change the default implemention to:

template <class T> inline T MASK(int width, ...)
{ /* same as above */ }

then declare the replacement this way:

template <class T>


inline typename T::basetype MASK(int width, typename T::basetype* = 0)
{

return MASK<typename T::basetype>(width);
}

in this way you get SFINAE to do what you need. Notice that the second
implementation is an overload and not a specialization. The extra
parameter is used to resolve ambiguities. Here's how it works:

* For types without T::basetype, SFINAE will discard the second overload
and the compiler will select the first one

* For types with T::basetype the compiler has the choice between the two
overloads, but "T::basetype*=0" is preferred over "..."

Hope it helps,

Alberto

Alberto Barbati

unread,
Sep 19, 2004, 6:52:35 AM9/19/04
to
Rob Williscroft wrote:
>
> template < typename T >
> inline typename T::basetype
> MASK(int width, typename T::basetype * = 0 )
> {
> return MASK<typename T::basetype>(width);
> }
>
> The extra argument (typename T::basetype * = 0) *isn't* required
> for a conforming compiler, but it helps with gcc 3.2, sorry
> I don't have 2.95 so I can't check.
>

What makes you say that? The extra parameter *is* required even on
conforming compilers. Without the extra parameter, for a T that has
T::basetype, you will get two overloads with exactly the same signature
apart from the return value:

T MASK(int)
T::basetype MASK(int)

Both overloads are equally good and the compiler will complain about the
ambigous call. (Notice that those are overloads and resolved as such,
there is no such thing as partial ordering of specializations for
function templates).

Even with the extra parameter, your suggestion is still incomplete,
because the compiler would have to choose between these two signatures:

T MASK(int)
T::basetype MASK(int, T::basetype* = 0)

again, no one is better than the other. Changing the declaration of the
first template by adding "..." as I said in my other post, solves the issue.

Alberto

Rob Williscroft

unread,
Sep 20, 2004, 1:52:36 AM9/20/04
to
Alberto Barbati wrote in news:YWV2d.9584$zF3.2...@twister2.libero.it
in comp.lang.c++.moderated:

> Rob Williscroft wrote:
> >
> > template < typename T >
> > inline typename T::basetype
> > MASK(int width, typename T::basetype * = 0 )
> > {
> > return MASK<typename T::basetype>(width);
> > }
> >
> > The extra argument (typename T::basetype * = 0) *isn't* required
> > for a conforming compiler, but it helps with gcc 3.2, sorry
> > I don't have 2.95 so I can't check.
> >
>
> What makes you say that?

A mistake on my part.

> The extra parameter *is* required even on
> conforming compilers. Without the extra parameter, for a T that has
> T::basetype, you will get two overloads with exactly the same
> signature apart from the return value:
>
> T MASK(int)
> T::basetype MASK(int)
>
> Both overloads are equally good and the compiler will complain about
> the
> ambigous call. (Notice that those are overloads and resolved as
> such,
> there is no such thing as partial ordering of specializations for
> function templates).
>

Agreed.

> Even with the extra parameter, your suggestion is still incomplete,

Yes, and unfortunatly your solution has the same problem.

> because the compiler would have to choose between these two
> signatures:
>
> T MASK(int)
> T::basetype MASK(int, T::basetype* = 0)
>
> again, no one is better than the other. Changing the declaration of
> the first template by adding "..." as I said in my other post, solves
> the issue.
>

No, ... plays no part in overload resolution, as it isn't the target
of a conversion.

#include <iostream>
#include <ostream>

void f( int, ... )
{
std::cout << "f( int, ... )\n";
}

void f( int, int = 0 )
{
std::cout << "f( int, 0 )\n";
}

int main()
{
f( 1 );
}

Unfortunatly I have no solution for the OP as I can't get
a "has_type_member< T >" trait to work with gcc 3.2 or 3.3.

If 3.4 and better is an option:


#include <iostream>
#include <ostream>

struct composite
{
int i;
typedef int basetype;
};


#define DEFINE_HAS_TYPE_IMP( Name, Member ) \
template < typename T > \
struct Name \
{ \
private: \
template < typename > struct empty_t_; \
template < typename U > \
static char helper_f( U *, empty_t_< typename U::Member > * = 0 ); \
static char (&helper_f( ... ))[2]; \
\
public: \
enum { value = \
sizeof( helper_f( (T *)0 ) ) == sizeof( char ) \
}; \
};
/**/
/*
----------------------------------------------------------------------- */

DEFINE_HAS_TYPE_IMP( has_basetype, basetype )

template < bool > struct disable_type {};
template <>
struct disable_type< false >
{
typedef void type;
};


template <class T>
inline T MASK(

int width,
typename disable_type< has_basetype< T >::value >::type * = 0
)
{
std::cout << "T width = " << width << "\n";
return width * width;
}

template < typename T >
inline typename T::basetype

MASK( int width, typename T::basetype * = 0 )
{
std::cout << "T::basetype \n";


return MASK< typename T::basetype >( width );
}

int main ()
{
std::cout << "MASK<int>(5)\n";
MASK< int >(5);

std::cout << "MASK<char>(3)\n";
MASK<char>(3);

std::cout << "MASK<unsigned char>(4\n";
MASK<unsigned char>(4);

std::cout << "MASK<composite>(7)\n";
MASK<composite>(7);
}

Rob.
--
http://www.victim-prime.dsl.pipex.com/

Carl Barron

unread,
Sep 20, 2004, 2:04:51 AM9/20/04
to
Alan Lehotsky <a...@alum.mit.edu> wrote:

> I'm trying to write a templated function with the following behavior.
>
> Given a type T and an integer width, write a function that creates a
> constant of either type T (for POD T) or of type T::basetype for any
> aggregate type having a T::basetype that's a POD. The constant is
> composed of a bitstring of length 'width'.
>
> The following works as long as I don't try to handle aggregate types.
> But SFINAE doesn't stop gcc (2.95.3 or 3.4.2) from complaining about
> the specialization
>
> template<> inline T::basetype MASK<T>(int width) ....
>
> producing about 80 lines of errors all told. I've elided some that I'm
> pretty sure are irrelevant. How can I make the return type of the
> function do the right thing here?

I am guessing you need a built in integral type for one function and a
struct/class with a member type basetype for the other, if neither of
thess conditions occurs, then its an error.

boost provides is_integral to determine if we have an integral type T.
it also provides enable_if<bool,T> which we can use to generate a
compile time error if does not contain a member type basetype.

We have two functions with different return types so once we determine
which case we have integral, has basetype member, or error condition
then we are done.

template <class T>
struct Mask1
{
static T mask(int);
};

template <class T>
struct Mask2
{
static typename T::basetype mask(int);
};

template <bool B,class T>
struct ret_type {typedef T type;};

template <class T> struct ret_type<false,T>
{
typedef typename T::basetype type;
};

template <class T>
inline
typename ret_type<boost::is_integral<T>::value,T>::type
Mask(int width)
{
typedef typename boost::if_c
<
boost::is_integral<T>::value,
Mask1,
Mask2
>:: mask_type;

return mask_type::mask(width);

Alberto Barbati

unread,
Sep 20, 2004, 2:29:54 PM9/20/04
to
Rob Williscroft wrote:

> Alberto Barbati wrote in news:YWV2d.9584$zF3.2...@twister2.libero.it
>

> > Even with the extra parameter, your suggestion is still incomplete,
>
> Yes, and unfortunatly your solution has the same problem.
>
> > because the compiler would have to choose between these two
> > signatures:
> >
> > T MASK(int)
> > T::basetype MASK(int, T::basetype* = 0)
> >
> > again, no one is better than the other. Changing the declaration of
> > the first template by adding "..." as I said in my other post, solves
> > the issue.
> >
>
> No, ... plays no part in overload resolution, as it isn't the target
> of a conversion.
>

I guess you're right. I was deceived by VC7.1 that compiles my solution
without complaints and producing the desired behaviour. So strange,
maybe a compiler bug? Btw, Comeau (online) agrees with you.

Alberto

Mike Alexeev

unread,
Sep 20, 2004, 3:13:04 PM9/20/04
to
Alan Lehotsky <a...@alum.mit.edu> wrote in message
news:<apl-039B6A.2...@news6.east.earthlink.net>...


Alan,

All you need is a helper struct to define the return type based on the
template type:

template <class T>
struct Result
{
typedef T type;
};
template <>
struct Result<composite>
{
typedef composite::basetype type;
};

template <class T> inline typename Result<T>::type MASK (int width) {
typedef typename Result<T>::type type;
if (width < 0 || width > (sizeof(type)*8))
return type ();
else if (width == (sizeof(type)*8))
return ~type ();
else
return type(~((~type()) << width));
}
Hope, this helps.
---
Thanks,
Mike Alexeev

Rob Williscroft

unread,
Sep 21, 2004, 7:25:33 AM9/21/04
to
Alberto Barbati wrote in news:YWV2d.9584$zF3.2...@twister2.libero.it in
comp.lang.c++.moderated:

> Both overloads are equally good and the compiler will complain about the
> ambigous call. (Notice that those are overloads and resolved as such,
> there is no such thing as partial ordering of specializations for
> function templates).
>

Its irrelevent to the OP's problem (and my broken solution to it) but
I did find:

14.5.5.2 Partial ordering of function templates.

Its no use in the case under descusion as it uses a transformation
that ignores return types (except in the case of operator type()).

Rob.
--
http://www.victim-prime.dsl.pipex.com/

0 new messages