7

Would any one knows according to what rules code below doesn't compile?

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

template<class T>
struct X
{
};
template<class T>
struct X<B<T>::type*>//HERE I'M PARTIALLY SPECIALIZING (WELL, TRYING TO...)
{
};

Please see comment inside the code.

There is nothing we can do
  • 23,727
  • 30
  • 106
  • 194

4 Answers4

10

How do you think that will work? The compiler will look to see if there is a class T somewhere that has a typedef "type" to your class?

It just won't. Even though it's a pointer.

Remember that presumably your B template is presumably specialised in places so that type is not always T*, but it can't deduce it with reverse engineering.

For those who did not understand my answer fully, what you are asking the compiler to do is find a class U such that B::type is the class you pass in as a parameter.

class Foo;
class Bar;

template<> struct B<Foo>
{
  typedef int type;
};

template<> struct B<Bar>
{
  typedef int type;
};

X<int*> // ambiguous, T is Foo or Bar?

It is difficult to know exactly why you are trying to do what you are. You can do a partial specialization on all pointers and then a total specialization on specific pointers, which could be implement in terms of another template.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • 1
    I don’t really understand this answer. The compiler *knows* which class to use – `B`. Whenever `X` is instantiated, the compiler is absolutely capable of searching through all partial specializations of `X` for the most specific. In fact, that’s what it *always* does. Consequently, Nawaz’ answer is correct. – Konrad Rudolph Mar 06 '11 at 15:15
  • (Continued) Or rather, I’d *expect* Nawaz’ answer to be correct. Clearly it doesn’t work but I fail to understand the compiler’s complaint (GCC’s message is similar to VS 2010’s). – Konrad Rudolph Mar 06 '11 at 15:22
  • @Kondrad Nawaz's answer cannot be correct if it doesn't compile, that's what I would think anyway. – There is nothing we can do Mar 06 '11 at 15:23
  • @Kondrad to your last comment: That exactly what I'm stuck with. According to which "bullet" in std this doesn't compile? – There is nothing we can do Mar 06 '11 at 15:25
  • @Konrad and @There is nothing : I'm also wondering what my code isn't compiling, although it seems correct to me. http://ideone.com/iwnuE – Nawaz Mar 06 '11 at 15:36
  • The compiler does not know what class to use. It is looking for a class U such that B::type is T. It may happen that U=T but there is no such guarantee. – CashCow Mar 06 '11 at 15:52
  • @CashCow “It is looking for a class U such that B::type is T” – Why should it? `T` is bound and well-known in the context so it should look for `B::type`, not for `B::type` for some `U`. The compiler should be able to perform a straightforward substitution here. – Konrad Rudolph Mar 06 '11 at 16:13
  • @Konrad look at the definition of the template, it is not X it is X::type> the T in the template definition above is deceptive. In my example you would write X not X or X – CashCow Mar 06 '11 at 18:27
6

You need to use typename keyword as,

template<class T>
struct X<typename B<T>::type*>
{
};

It's because B<T>::type is a dependent name. So typename is required!

--

EDIT:

Even after putting typename, it isn't compiling. I think it's because deduction of type T in B<T> from X<U> is difficult, or possibly impossible, for the compiler. So I believe its non-deduced context.

See a similar example here and the discussion:

Template parameters in non-deduced contexts in partial specializations


However, if you change the specialization to this:

template<class T>
struct X<B<T> >
{
};

Then it becomes the deducible context, and so would compile.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • @Navaz I did as you proposed and I've got: Error 1 error C2764: 'T' : template parameter not used or deducible in partial specialization 'X::type*>' from VS 2010 – There is nothing we can do Mar 06 '11 at 15:11
  • @There is nothing. Because it can't. What you are trying to do is beyond the scope of the compiler. – CashCow Mar 06 '11 at 15:13
  • @CashCow what do you mean by beyond the scope of compiler (and please, I'm not trying to have a go with you or with anyone here, I'm just trying to understand) – There is nothing we can do Mar 06 '11 at 15:23
  • @There is nothing we can do: Please see my edit. And the link to a good topic discussing the same problem! – Nawaz Mar 06 '11 at 15:52
  • @Nawaz indeed your second one would be deducible, B can only ever mean one thing here. – CashCow Mar 06 '11 at 18:33
  • 1
    It should compile, I think. It just should never match that specialization. – Johannes Schaub - litb Mar 06 '11 at 19:38
  • @Johannes: The specialization doesn't compile. See this yourself : http://ideone.com/iwnuE – Nawaz Mar 07 '11 at 13:06
  • I said "It should compile". I didn't say "Ideone accepts this". It probably is a warning that was output as an error because of -Werror and if not then it's possibly a GCC bug. I'm talking from memory, so I might be wrong. See core issue 549 for the issue report for this http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#549 . You could apply rule 14.6p8 ("If no valid specialization can be generated..."), but that's a very weak rule to use for emitting a diagnostic here, in my opinion and I think it hasn't got non-deduced contexts of partial specializations in mind. – Johannes Schaub - litb Mar 07 '11 at 21:25
0

Assuming you already added typename as suggested by Nawaz.

The problem is exactly explained in the error message you encounter: "template parameter is not deducible in partial specialization B<T>::type*. The problem is that B<T>::type and T is exactly the same for all types T. Consider the following example:

class MyClass1 {};
typedef typename B<MyClass>::type MyClass2; //(*)

X<MyClass*> obj1;
X<MyClass2*> obj2;

The result of line (*) is a type MyClass2 which is essentially MyClass1. So, obj1 and obj2 should be objects of the same class. Now, which version of template X should they use?

If you would expect the specialised version of X, tell me if the answer should be the same if line (*) is removed (and obviously obj2 as well). Still obj1 should be the specialised version of X as line (*) has nothing to do with it.

But now we expect the compiler to detect that some type can be potentially declared as B<T>::type although we never do this. We expect the compiler to verify all possible template instantiations to check if there is no strange typedef in one of them.

I hope this clarifies why such specialisation cannot be handled by the compiler.


An alternative that might help

I believe your problem could be attacked by creating a trait class for explicitly marking types that should be handled in a special way. Something like this:

template <bool v>
struct boolean_value {
  static const bool value=v;
};

template <typename T>
struct is_my_interesting_type : public boolean_value<false> {};

class MyClass {
  ...
};

template <>
struct is_my_interesting_type<MyClass> : public boolean_value<true> {};

template <typename T, bool special>
class  InternalX {
  ... //generic version of your template X
};

template <typename T>
class InternalX<T,true> {
  ... //special version of your template X
};

template <typename T>
class X : public InternalX<T,is_my_interesting_type<T>::value> {};

Also, you might be interesting how it is done in boost library, in particular Boost.Type_Traits

CygnusX1
  • 20,968
  • 5
  • 65
  • 109
  • “The problem is that `B::type` and `T` is exactly the same for all types `T`” – No. That is *not* a problem (or should not be). “Now, which version of template X should they use?” – Obviously the specialized version because your types are both pointers. The usage of `B::type` instead of just `T` here is completely irrelevant. What’s relevant is that the specialization is a *pointer*. – Konrad Rudolph Mar 06 '11 at 16:16
  • If you want to have specialised version for all pointeres you should have `X` and not `X::type*>`. The latter suggests that you want the specialisation only for _some_ pointers but not all of them. _That_, and not the fact that you have a pointer, is a problem. You will get exactly the same error if you remove `*`. – CygnusX1 Mar 06 '11 at 16:26
  • "The usage of B::type instead of just T here is completely irrelevant" -> it is relevant and crucial for the compiler. It cannot deduct what `B` is needed to get a match with current type that you want instantiate with. Morover, he doesn't even know that he should actually look into template class B when searching for a match. – CygnusX1 Mar 06 '11 at 16:34
  • Why is it a problem though? Why can it not deduce `B`? (In fact, there is no deduction, it’s a straightforward replacing). – Konrad Rudolph Mar 06 '11 at 17:53
  • Imagine you have 1000 template classes which have typedefs inside. Do you think the compiler will check all of them for a match? And what if there are more than one match? – CygnusX1 Mar 06 '11 at 22:46
0

"The argument list cannot be identical to the non-specialized argument list (it must specialize something)" see partial_specialization at en.cppreference.com