views:

286

answers:

3

I have the following code (sorry for the large code chunk, but I could not narrow it down any more)

template <bool B>
struct enable_if_c {
      typedef void type;
};

template <>
struct enable_if_c<false> {};

template <class Cond>
struct enable_if : public enable_if_c<Cond::value> {};

template <typename X>
struct Base { enum { value = 1 }; };

template <typename X, typename Y=Base<X>, typename Z=void>
struct Foo;

template <typename X>
struct Foo<X, Base<X>, void> { enum { value = 0 }; };

template <typename X, typename Y>
struct Foo<X, Y, typename enable_if<Y>::type > { enum { value = 1 }; };

int main(int, char**) {
        Foo<int> foo;
}

but it fails to compile with gcc (v4.3) with


foo.cc: In function ‘int main(int, char**)’:
foo.cc:33: error: ambiguous class template instantiation for ‘struct Foo<int, Base, void>’
foo.cc:24: error: candidates are: struct Foo<X, Base<X>, void>
foo.cc:27: error:                 struct Foo<X, Y, typename enable_if<Y>::type>
foo.cc:33: error: aggregate ‘Foo<int, Base<int>, void> foo’ has incomplete type and cannot be defined

ok, so it's ambiguous. but I was'nt expecting it to be a problem as when using specialization it will almost always be some ambiguity. However this error is only triggered when using the class with enable_if<...>, if I replace it with a class like the following there is no problem.


template <typename X, typename Y>
struct Foo<X, Y, void > { enum { value = 2 }; };

why does this class not cause a ambiguity while the other do? isn't the two the same thing for classes with a true ::value? anyway, any hints as to what im doing wrong is appreciated.

Thanks for the answers, my real problem (to get the compiler to select my first specialization) was solved by replacing struct Foo<X, Base<X>, void> with struct Foo<X, Base<X>, typename enable_if< Base<X> >::type >. Which seems to work the way I want.

A: 

aren't you missing a

<

symbol ?

nairdaen
< and > was lost to the HTML parser of the site, hope I fixed it now.
keis
A: 

I think you are missing a '<', a template should look like:

template< typename T >
struct myStruct
{};

//OR

template< class T >
struct myStruct
{};
DeusAduro
Oh you fixed the code, this answer is redundant now.
DeusAduro
Why would you downvote me... I replied before the code was edited, at which point my answer made sense...
DeusAduro
Because it doesn't make sense *now*. If you don't want to edit to answer the new question, then delete your answer entirely.
avakar
And in any case, the missing <'es wouldn't be it at all, of course
Johannes Schaub - litb
+10  A: 

The gist of your question is that you have:

template <typename X, typename Y, typename Z>
struct Foo {};

template <typename X>
struct Foo<X, Base<X>, void> {};                   // #1

template <typename X, typename Y>
struct Foo<X, Y, typename whatever<Y>::type> {};   // #2

and you're trying to match it to

Foo<int, Base<int>, void>

Obviously, both specializations match (the first with X = int, the second with X = int, Y = Base<int>).

According to the standard, section 14.5.4, if there are more matching specializations, a partial ordering (as defined in 14.5.5.2) among them is constructed and the most specialized one is used. In your case, however, neither one is more specialized than the other. (Simply put, a template is more specialized than another, if you can replace each type parameter of the latter template with some type and in result get the signature of the former. Also, if you have whatever<Y>::type and you replace Y with Base<X> you get whatever<Base<X> >::type not void, i.e. there is not processing performed.)

If you replace #2 with

template <typename X, typename Y>
struct Foo<X, Y, void > {};                        // #3

then the candidate set again contains both templates, however, #1 is more specialized then #3 and as such is selected.

avakar
this answers my question, but my core problem remains.I dunno how follow up questions is supposed to be done here, but I'll just try a comment.is there a way to get what I want, to get #1 selected when instansiating "Foo<int>"? like tricking the compiler to think that #1 is more specialized perhaps.
keis
+1, where a +10 would be more appropriate. It took me so long to write the answer that you beat me by *only* 29 minutes :)
David Rodríguez - dribeas
@keis: The problem is that specialization is a partial order, and in some cases two elements are really not ordered. Now, if you state what you are really trying to achieve (write a template such that when instantiated with int will have a value of 1 while when instantiated with X will have a value of 2) then you may get better hints
David Rodríguez - dribeas
keis, if you have follow ups, just edit the question. You can leave a comment at some promising answers that you've done so. Generally, you don't accept (green-box) an answer until you're completely satisfied.
avakar
dribeas, sorry for that, I feel a little ashamed for such a short answer. :)
avakar
keis, as dribeas have said, you can't trick the compiler. Tell us what you really want to do.
avakar
@avakar: your answer is great: simple clear and concise. what else can you ask for? As I commented above, it is just a pity that you cannot give extra credit. There are many simple answers (how to copy a string in C) that get many more upvotes than this much harder answer is ever going to get.
David Rodríguez - dribeas
I still don't understand this in its entirety. I think partially because partial ordering is greatly underspecified in c++03 (luckily, they added much stuff in c++1x that explains it better, but still some issues are left open to me). I would be glad if you could delve into it a bit more. Why would `whatever<Base<X> >::type` not be `void`? Both seem to name the `void` type. Also what i don't get is, what is an `unique type`? Is it a type created exactly for this purpose and not identical to any other type? I understand partial ordering for other examples, but this code gets me stuck, frankly.
Johannes Schaub - litb
litb, the point is you can tell whether one template specialization is more specialized than another without seeing how it's instantiated. The partial ordering is determined by the definitions, before the templates are instantiated. That's why the fact that `whatever<Base<X> >::type == void` does not come to play here. What's `unique type`? Is that in the standard? I don't have it handy at the moment, but I'll take a look at it and attempt to clarify.
avakar
Well the procedure is, that for each type parameter, a "unique type" is taken and put into any place of the parameter list of one parameter. Using the resulting parameter list as an argument list, "type deduction" is done against the other function template parameter list. In C++03, it's left unspecified what "type deduction" means in this context. C++1x defines this, luckily. So it takes `Foo<Q, Base<Q>, void>` and type-deducts against `Foo<X,Y,typename whatever<Y>::type>`. `X=Q` and `Y=Base<Q>`. The last argument remains nondeduced, because it appears in a non-deduced context.
Johannes Schaub - litb
It seems to me that type deduction fails in this direction. Doing the other direction, we will have an argument of type `Foo<Q, R, whatever<R>::type>`. If `unique` type means just a naked type without any special properties, then this can't work either, because the type wouldn't have a `::value` member. Thus, construction of the argument list for checking the other direction already fails. An ordering therefor cannot be constructed. But now, this depends on what `unique type` really means, obviously. It also depends on whether in the first direction, `whatever<Y>::type` really remains ...
Johannes Schaub - litb
... undeduced or whether `14.8.2.4` (c++'03) includes substituting the then known `Y` into it (which is `Base<Q>`) and would then yield `void` (the paragraph seems to indicate that this happens, but it is silent about saying this explicitly. this confuses me) If this happens, then in the first direction type deduction would succeed against the latter. Since type deduction fails (under my interpretation of "unique type") in the other direction against the former specialization, the former specialization would be more specialized. But this is not the case apparently. So the rule must be that ...
Johannes Schaub - litb
... places that could be filled because the types where parameters are used were deduced elsewhere are not filled, but are left open, so that type deduction then fails. But neither this rule nor the opposite is stated explicitly, so i'm left wondering about this -.- I hope i could explain what i'm thinking about, and it doesn't sound all too weird to be understandable. Hope you could clear up the matter later if you have teh standard at hand. Thanks!
Johannes Schaub - litb
I don't think that `::value` plays any role in this. Checking whether one type is more specialized than another is IMHO a simple search/replace algorithm (in the standard, of course, it takes several pages that are scattered all over the document). Anyway, perhaps you could open a question for this. I'll take a look at it as soon as I can.
avakar
I've re-read the relevant sections of the standard. "In C++03, it's left unspecified what "type deduction" means in this context." Template argument deduction is defined in 14.8.2 even in C++03. "then this can't work either, because the type wouldn't have a ::value member" As I understand it, there is no semantic processing performed, only syntactic replacement. In other words, synthesizing and substituting a unique type means simply replacing all occurences of `Y` with `Base<X>`, i.e. transforming `Foo<X, Y, typename whatever<Y>::type>` to `Foo<X, Base<X>, typename whatever<Base<X> >::type>`
avakar
The word "unique" in this case is probably redundant (I don't think it means "new", as it does in context of unnamed namespaces). Moreover, I don't think that `whatever<Base<X> >::type` is turned into `void`, because of 14.5.5.2/2: "Given two overloaded function templates, whether one is more specialized than another can be determined [...]", i.e. it can be determined without knowing the actual types involved.
avakar
@avakar, well it's defined as "type deduction for a function call", "type deduction for types" etc.. None of that has a section for "type deduction for partial ordering". This is what c++1x added. "it can be determined without knowing the actual types involved." well if it is void for any type (like in my new question), so it will always know that it's `void`. But it's what i'm unsure about. If it says "type", then it can't mean "syntactic form", though. i posted a question here: http://stackoverflow.com/questions/1180325/partial-ordering-with-function-template-having-undeduced-context :)
Johannes Schaub - litb
Thanks for your discussions. I think we will come to a solution :)
Johannes Schaub - litb