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

problem with partial ordering of classes

6 views
Skip to first unread message

Rodolfo Lima

unread,
Jul 25, 2010, 11:18:28 AM7/25/10
to
Hi, I'm having some problems with the following code, gcc-4.5 refuses
to compile it:

#include <iostream>

template <bool B, class V=void> struct enable_if {};
template <class V> struct enable_if<true,V> { typedef V type; };

template <class T>
struct bar {};

template <class T, int ID, class V=void>
struct foo;

template<template<class> class V, class T, int ID>
struct foo<V<T>, ID>
{
static const int value = 1;

};

template<class T, int ID>
struct foo<bar<T>,ID, typename enable_if<ID!=0>::type>
{
static const int value = 2;

};

int main()
{
std::cout << foo<bar<int>,1>::value << std::endl;

}

//------------------------------------------

The error message:
teste.cpp: In function ‘int main()’:
teste.cpp:26:30: error: ambiguous class template instantiation for
‘struct foo<bar<int>, 1>’
teste.cpp:14:1: error: candidates are: struct foo<V<T>, ID>
teste.cpp:20:1: error: struct foo<bar<T>, ID, typename
enable_if<(ID != 0)>::type>
teste.cpp:26:15: error: incomplete type ‘foo<bar<int>, 1>’ used in
nested name specifier

According to partial ordering rules, struct foo<bar<T>,ID, typename
enable_if<ID!=0>::type> is more specialized than struct foo<V<T>, ID>,
isn't it? So it should be used, instead of giving an ambiguity error.

Am I wrong or is the compiler?

Regards,
Rodolfo Lima


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

Daniel Krügler

unread,
Jul 25, 2010, 7:56:56 PM7/25/10
to

This time it's you ;-)

The problem is that in the attempt to define the partial
specialization

template<class T, int ID>

struct foo<bar<T>,ID, typename enable_if<ID!=0>::type>;

you are running into a non-deducible context for the last
argument. Unfortunately the standard does not require this
attempt to be ill-formed. There is a core issue concerning
this situation, see:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#549

HTH & Greetings from Bremen,

Daniel Krügler

Johannes Schaub (litb)

unread,
Jul 26, 2010, 10:07:46 AM7/26/10
to
Daniel Krügler wrote:

The issue report and his partial specializations are two separate things,
though. In this partial specialization, both T and ID are deducible from the
types of the specialization: The partial specialization thus matches. But
the partial specialization in the issue report can never match, thus the
concerns about the validity of the code in it.

I believe that partial ordering is a difficult thing to understand, and in
my opinion the Standard doesn't make it quite easy. I hope this time i get
it right, but i fear i have it all wrong. Let's see :) For the given code, i
think the following will happen:

* Rewrite them to function templates

=>

template<template<class> class V, class T, int ID>

void f(foo<V<T>, ID, void>); // T1

And

template<class T, int ID>

void f(foo<bar<T>, ID, typename enable_if<ID!=0>::type>); // T2

* Invent "unique" types, templates and values for the template parameters

void(foo<Vx<Tx>, IDx, void>); // XT1 (transformed T1)
void(foo<bar<Tx>, IDx, enable_if<IDx != 0>::type>) // XT2

For the function type of the transformed declaration, we keep the third
parameter dependent in case of enable_if, and apply the "equivalence" rule
according to 14.6.6.1 to compare its type identity. Now, we deduce XT1
against T2 and XT2 against T1. If both deductions fail or both succeed,
there is no partial order:

XT1 against T2 is:

foo<Vx <Tx>, IDx, void>
foo<bar<T >, ID, enable_if<ID!=0>::type> // T and ID

ID could be deduced to IDx, but "bar" does not match "Vx". The third part is
not compared to "void" because it's a non-deduced context.

XT2 against T1 is:

foo<bar<Tx>, IDx, enable_if<IDx != 0>::type>
foo<V <T >, ID, void> // V, T and ID

We deduce V to bar, T to Tx, but we are not able to match "void" (which is
not a non-deduced context) to "enable_if<...>::type".

Both deduction runs fail, so we have no order.

Daniel Krügler

unread,
Jul 26, 2010, 10:13:08 AM7/26/10
to
On 26 Jul., 01:56, Daniel Krügler <daniel.krueg...@googlemail.com>
wrote:

I was a bit too quick here: My analysis was wrong, because
ID has already been deduced. So let's declare the two test
overloads to verify partial ordering:

template<template<class> class V, class T, int ID>

void test(foo<V<T>, ID>);

template<class T, int ID>

void test(foo<bar<T>,ID, typename enable_if<ID!=0>::type>);

I agree that the ambiguity is astonishing. I have the feeling you
are running into core issue

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#455

which describes problems with partial ordering in the presence
of non-deduced arguments, but I'm not 100% sure.

Rodolfo Lima

unread,
Jul 26, 2010, 1:48:26 PM7/26/10
to
On Jul 26, 11:07 am, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:

> XT2 against T1 is:
>
> foo<bar<Tx>, IDx, enable_if<IDx != 0>::type>
> foo<V <T >, ID, void> // V, T and ID
>
> We deduce V to bar, T to Tx, but we are not able to match "void" (which is
> not a non-deduced context) to "enable_if<...>::type".

Here's what I thought: since all template parameters were deduced from
the first 2 arguments, the compiler would apply the deduced ID into
enable_if<ID!=0>::type, yielding 'void', and voilà, void matches to
void.

The question is: is there any side-effects to treat non-deduced
contexts during partial ordering this way, namely:
1- deduce all template parameters from arguments are in the form that
allows deduction (14.8.2.4.9). If at least one parameter isn't
deduced, well, it's a deduction failure.
2- apply all deduced parameters into arguments that falls into non-
deduced context and evaluate whichever compile-time expressions might
appear
3- use the resulting function signature to verify if there's a match

[]s,
rod

Message has been deleted

Johannes Schaub (litb)

unread,
Jul 26, 2010, 1:48:13 PM7/26/10
to
Daniel Krügler wrote:

Indeed, I think the problem that guy describes in that issue report is
similar to the problem we have here. I've analyzed the first case of the
issue report, and i come to the same conclusion as comeau: The code seems to
be valid:

void(T, typename A<T>::type) // T1
void(T*, typename A<T>::type*) // T2

void(Tx, A<Tx>::type) // XT1
void(Tx*, A<Tx>::type*) // XT2


XT1 -> T2

void(Tx, A<Tx>::type)
void(T*, typename A<T>::type*)

=> T* does not match Tx, second param non-deduced context


XT2 -> T1

void(Tx*, A<Tx>::type*)
void(T, typename A<T>::type)

=> T = Tx*, second param non-deduced context

XT2 -> T1 OK, T2 at least as specialized as T1
XT1 -> T2 FAIL

=> T2 more specialized than T1.


Clang and GCC don't seem to do partial ordering in the first place, i think.
There is a difference between these two sets of compiler behaviors with
regard to handling non-deduced contexts during argument deduction itself - i
don't believe the difference is due to partial ordering. I have done two
examples:

template<typename T>
struct id { typedef T *type; };

template<typename T>
void f(T, typename id<T>::type);

int main() { f(0, 0); }

And

template<typename T>
struct id { typedef T type; };

template<typename T>
void f(T, typename id<T>::type*);

int main() { f(0, 0); }

The only difference is the placement of the star. Comeau accepts both
examples, because in both cases "int" is not compared to the non-deduced
context, which i think is the correct behavior. GCC and Clang however only
accept the first, for reasons unknown to me. They seem to think that a
pointer cannot possibly match an int and reject, maybe thinking that this
"dependent-type *" is a compound type, and they are allowed to do so
(paragraph 6). I don't think that's correct though. The allowance of the
Standard for compound types to contain both deduced and non-deduced contexts
does not seem to apply to a ptr-declarator being still allowed to be checked
- the phrase "compound types" seems to refer to the list in
14.9.2.5[temp.deduct.type]/3 - not to the general one in 3[basic] (and then
interpreting a star piece as a "type" is still quite a stretch).

There are two cases that i know of that can prevent comparison:

- An argument that contains only explicitly specified arguments/contains no
deduced arguments (14.9.1[temp.arg.explicit]/6)
- Nondeduced context (14.9.2.5[temp.deduct.type]/4)

The first is handled consistently also by Clang and GCC:

template<typename T> void f(T);
int main() { f<int*>(0); }

0 new messages