From: Mikosz on
Hi all,

I've been wondering for a couple of days now - why won't the following
code compile (fails on both gcc and comeau online):

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


template <typename T1, typename T2>
struct A
{
};

template<typename X, typename T>
struct A< X, typename X::template Y<T> >
{
typedef int Type;
};


int main()
{
typedef X::Y<int> YI;
A<X, YI>::Type t; // <- COMPILATION ERROR
return 0;
}

The compilation error says that A::Type is undefined. Now, if you
change the code, so that both versions of A contain a constant of the
same name, but with different value and you'll print it out in main(),
you'll find that the code does compile, but the unspecialised template
version is chosen.

Since the templates compile just fine, it means that the specialised
version is indeed a proper specialisation and is more detailed than
the unspecialised one. How do I make the compiler select the
specialised version then? If the construction is invalid and it's
impossible to use the specialised version - why does the code compile
at all?

C++ gurus - what say you? :)

Cheers,
Mikolaj

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

From: Andy Venikov on
Mikosz wrote:
> Hi all,
>
> I've been wondering for a couple of days now - why won't the following
> code compile (fails on both gcc and comeau online):
>
> struct X
> {
> int t;
> template <typename T>
> struct Y
> {
> T t;
> };
> };
>
>
> template <typename T1, typename T2>
> struct A
> {
> };
>
> template<typename X, typename T>
> struct A< X, typename X::template Y<T> >
> {
> typedef int Type;
> };
>
>
> int main()
> {
> typedef X::Y<int> YI;
> A<X, YI>::Type t; // <- COMPILATION ERROR
> return 0;
> }
>
> The compilation error says that A::Type is undefined. Now, if you
> change the code, so that both versions of A contain a constant of the
> same name, but with different value and you'll print it out in main(),
> you'll find that the code does compile, but the unspecialised template
> version is chosen.
>
> Since the templates compile just fine, it means that the specialised
> version is indeed a proper specialisation and is more detailed than
> the unspecialised one. How do I make the compiler select the
> specialised version then? If the construction is invalid and it's
> impossible to use the specialised version - why does the code compile
> at all?
>
> C++ gurus - what say you? :)
>
> Cheers,
> Mikolaj
>


The problem with this code is that your specialization of A uses a non-deduced context and hence never gets instantiated.

If your goal is to specialize template A on types that have a template struct Y, then you can achieve it this way:

template <typename T, typename Enable = void>
struct A
{
};

template <typename T>
struct type2void
{
typedef void type;
};


template <typename X>
struct A<X,
typename type2void<typename X::template Y<int> >::type >
{
typedef int Type;
};


//then in your main:
A<X>::Type t; //works fine.


The idea is to disable all instantiations of A whith type T where T::template<int> does not exist.

HTH,
Andy.

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

From: Paul Bibbings on
Mikosz <mikoszrrr(a)gmail.com> writes:

> Hi all,
>
> I've been wondering for a couple of days now - why won't the following
> code compile (fails on both gcc and comeau online):
>
> struct X
> {
> int t;
> template <typename T>
> struct Y
> {
> T t;
> };
> };
>
>
> template <typename T1, typename T2>
> struct A
> {
> };
>
> template<typename X, typename T>
> struct A< X, typename X::template Y<T> >
> {
> typedef int Type;
> };
>
>
> int main()
> {
> typedef X::Y<int> YI;
> A<X, YI>::Type t; // <- COMPILATION ERROR
> return 0;
> }

In your specialization of struct A you may have one-two-many template
parameters, depending on what your intentions are. If you make this:

template< typename T >
struct A< X, typename X::template Y< T > >
{
typedef int Type;
}

then compilation succeeds.

Because of the commonality of names, you might be thinking that your
version of the specialization is specialized on your struct X, but of
course it is not. Having provided the first of your template parameters
and called it X does not gain you anything over having defined this as:

template< typename Foo, typename T >
struct A< Foo, typename Foo::template Y< T > >
{
typedef int Type;
}

The question then remains why /this/ is not considered a better match
over the primary template for A< X, YI >, and it would require me to pay
close attention to the details of a number of subclauses of the standard
in order to provide the appropriate references to answer that one, which
I'm not able to do at the moment.
Of course, if you /are/ aware of the exact form of your specialization
and are asking your question in the knowledge of that, then nothing said
here progresses you any further beyond what you already know! :-)

Regards

Paul Bibbings

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

From: Johannes Schaub (litb) on
Mikosz wrote:

> Hi all,
>
> I've been wondering for a couple of days now - why won't the following
> code compile (fails on both gcc and comeau online):
>
> struct X
> {
> int t;
> template <typename T>
> struct Y
> {
> T t;
> };
> };
>
>
> template <typename T1, typename T2>
> struct A
> {
> };
>
> template<typename X, typename T>
> struct A< X, typename X::template Y<T> >
> {
> typedef int Type;
> };
>
>
> int main()
> {
> typedef X::Y<int> YI;
> A<X, YI>::Type t; // <- COMPILATION ERROR
> return 0;
> }
>
> The compilation error says that A::Type is undefined.

Yes. The issue here is that `typeame X::template Y<T>` is a nondeduced
context for both X and T. See 14.8.2.4/4 for what a non-deduced context is.

"T" can't be deduced unless X is known, which itself can't be deduced
using that parameter. After deduction is done, X is substituted there,
but "T" is still left undeduced, and a deduction failure occurs.

"If a template parameter is used only in nondeduced contexts and is not
explicitly specified, template argument deduction fails."

> Now, if you
> change the code, so that both versions of A contain a constant of the
> same name, but with different value and you'll print it out in main(),
> you'll find that the code does compile, but the unspecialised template
> version is chosen.
>
> Since the templates compile just fine, it means that the specialised
> version is indeed a proper specialisation and is more detailed than
> the unspecialised one. How do I make the compiler select the
> specialised version then? If the construction is invalid and it's
> impossible to use the specialised version - why does the code compile
> at all?
>

It is a proper specialization that is never taken because it never
"matches" the argument list. See 14.5.4.1/2. You can deduce T as a whole,
and then check whether it's a specialization of a member-template of X:

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

template <typename T1, typename T2, typename = void>
struct A
{
};

template<typename X>
struct hasit {

template<typename T>
struct check { };

/* notice: X is known, doesn't need deduction for check! */
template<typename T>
struct check< typename X::template Y<T> > {
typedef void does;
typedef T arg1_type;
};

};

template<typename X, typename T>
struct A< X, T, typename hasit<X>::template check<T>::does >
{
typedef int Type;
};

Now, because the outer X is already known, you are one step ahead in
deduction, and can immediately compare both Y<T>'s for compatibility
and deduce T right away.

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

From: red floyd on
On May 7, 11:15 am, Mikosz <mikosz...(a)gmail.com> wrote:
> Hi all,
>
> I've been wondering for a couple of days now - why won't the following
> code compile (fails on both gcc and comeau online):
>
> struct X
> {
> int t;
> template <typename T>
> struct Y
> {
> T t;
> };
>
> };
>
> template <typename T1, typename T2>
> struct A
> {
>
> };
>
> template<typename X, typename T>
> struct A< X, typename X::template Y<T> >
> {
> typedef int Type;
>
> };
>
> int main()
> {
> typedef X::Y<int> YI;
> A<X, YI>::Type t; // <- COMPILATION ERROR
> return 0;
>
> }
>

It's a dependent name. You need

typename A<X, YI>::Type t;

See FAQ 35.18

http://parashift.com/c++-faq-lite/templates.html#faq-35.18


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