Page 1 of 1

C++ Template Specialisation Problem

Posted: Mon Oct 01, 2007 1:41 am
by Schultz
I have been programming in C++ for a long time, but recently I stepped in a difficult problem with the language.

I have defined a type trait for to be used with a type defined as an inner class, but could not specialise the template of the trait.

The code below is my problem in its simplest form. It has been tested with gcc 4 (gentoo linux) and a gcc 3 (latest MinGW). It should not compile, just yield an error indicating the type. My intention was that it returned true_type in the compiler's error messages, but it returns false_type.

Code: Select all

struct	false_type {};
struct	true_type {};

template<typename T>	struct	trait {
	typedef		false_type		value;
};

template<typename T>	struct	X {
	struct	Y {
	};
};

template<typename T>	struct	trait< X<T> > {
	typedef		true_type		value;
};

//template<>
template<typename T>	struct	trait< typename X<T>::Y > {
	typedef		true_type		value;
};

int	main() {

	trait< X<int>::Y >::value::error_member;

	return	0;
}

Removing or leaving the template in the comment changes nothing.

Can anyone please help me? This is getting to be the bottleneck in my projects.

I know this is not the most appropriate place to post this, but I could find nowhere else.

Thanks in advance.

Posted: Mon Oct 01, 2007 4:08 pm
by maxdiver
I think, one of the template specializations is not correct:
template<typename T> struct trait< typename X<T>::Y > {
typedef true_type value;
};
First of all, I could not understand it.
Secondly, my VC 2005 SP1 could not understand it too ;)
error C2764: 'T' : template parameter not used or deducible in partial specialization 'trait<X<T>::Y>'

Really, what do you mean in "typename X<T>::Y"?
You tried to use this in such way:
trait< X<int>::Y >

But, in common case "trait<TT>", how compiler can check, if TT is X<T>::Y or not? Should compiler brute all types T, or maybe should it search a path in graph? :)

Posted: Mon Oct 01, 2007 8:48 pm
by Schultz
I think, one of the template specializations is not correct:

Code: Select all

template<typename T> struct trait< typename X<T>::Y > {
        typedef true_type value;
}; 
This is the one I am not sure about. Nevertheless, it compiles with no warnings in my gcc 4. Microsoft's compiler is known for getting nothing right when it comes to more complex codes, so I am willing to retain compatibility with gcc.
error C2764: 'T' : template parameter not used or deducible in partial specialization 'trait<X<T>::Y>'
I guess this can be the source of the problem. It says the specialization could not be deduced. But why? How am I to implement this specialization then?
Really, what do you mean in "typename X<T>::Y"?
It is standard syntax. When using scopes ( :: ) within templates to access types, a typename should be placed before the clause. The compiler complains of the absence of this keyword in many cases.
But, in common case "trait<TT>", how compiler can check, if TT is X<T>::Y or not? Should compiler brute all types T, or maybe should it search a path in graph?
I know nothing of compiler implementations, but I get fascinated by how nice is the C++ metaprogramming system (printing prime lists in compiler error mesages). The metaprogramming system is Turing complete.

Posted: Tue Oct 02, 2007 9:23 pm
by maxdiver
Of course, you may program C++ and don't matter about compiler implementation ;)

But let's try to think logically.
Here:
typename X<T>::Y
How compiler should choose T? There is no explicit way to choose T. Imagine this situation: you write Y, and there are several specializations of structure X, some of which contain Y, and some don't contain Y. What can compiler do in this situation? Of course, nothing.

I tried to explain, to prove, that type "T" is really not deducible.

P.S. I also don't rely on Microsoft compiler, as well as on other compiler. I tried to think logically, following Standard.

Posted: Tue Oct 02, 2007 10:26 pm
by Schultz
Oh, I got it. You mean X<int>::Y is the same as X<float>::Y.

But what if I put something like typedef T type; inside Y? Then I should be able to solve this.

The problem is that I can not get this working even then.

Posted: Wed Oct 03, 2007 1:33 pm
by maxdiver
I can't understand your idea, why you want to use this construction.

I found only two solutions, but both of them are not very good:
1) specialize traits<X<T>::Y> for each T manually
2) declare another traits_X<>:

Code: Select all

template<typename T>   struct   trait_X  { 
   typedef      typename X<T>::Y        Y; 
   typedef      true_type      value; 
}; 

int   main() { 
   trait_X< X<int>::Y >::value::error_member; 
   return   0; 
}

It seems to me, that there are no other ways. Because you can't specialize template (traits<>) as another template (traits< X<T>::Y >).


The only advice I can make - change structure of your program.

Posted: Wed Oct 03, 2007 3:38 pm
by Schultz
My goal in this construct is metaprogramming. For instance, I could define integral types as those whose members are either machine types or integral types and do not have default constructors or destructors implemented.

Then, instead of trait, I could name the template pointed_value_is_integral.

Then I could specialize algorithms like:

Code: Select all

// optimised copy

// general case
template<typename I, typename T> inline void __copy(I a, I b, I c, T) {
    for (; a != c; ++a, ++c) *a = *c;
}

// integral pointers
template<typename T> inline void __copy(T* a, T* b, T* c, true_type) {
    memcpy(a, c, (b-a)*sizeof(T));
}

// user
template<typename I> inline void copy(I a, I b, I c) {
    __copy(a, b, c, typename pointed_value_is_integral<T>::value());
}
This is why this is so essential.

Posted: Wed Oct 03, 2007 8:30 pm
by Krzysztof Duleba
There's no way to tell user-defined types from built-ins in templates except by explicit specialization.

Posted: Wed Oct 03, 2007 9:49 pm
by maxdiver
OK. So you need template "pointed_value_is_integral".

As Krzysztof Duleba said, there is only one way - explicit initialization.

I can only present the case: I found just the same template (I've forgotten its name) in STL of Microsoft compiler, and it was implemented by explicit specialization.

Something like that:

Code: Select all

template <typename T>
bool pointed_value_is_integral (const T & right)
{
	return true;
}

template <>
bool pointed_value_is_integral (const signed int & right)
{
	return false;
}

template <>
bool pointed_value_is_integral (const unsigned int & right)
{
	return false;
}

template <>
bool pointed_value_is_integral (const signed long & right)
{
	return false;
}

...

Posted: Wed Oct 03, 2007 11:37 pm
by Schultz
I see.

So I will have to change my approach.

Thanks guys. I'll tell if I can figure ou a solution for this.