Thursday, March 24, 2011

Partial Specialization Question

I need a fresh pair of eyes.

This is obviously illegal, but it shows what I'm trying to do:

template <typename T, T> struct Foo
{
};

template <typename T> struct Foo <T, 0> //Obviously I can't do this.
{
};

Is there any way to wrap T or do something tricky so that this sort of thing can work?

Thanks!

From stackoverflow
  • Yes, you can use this trick:

    template <typename T, T, T=0> struct Foo {
    };
    
    template <typename T, T t> struct Foo <T, t, t> {
    };
    

    If t is 0 in the specialization, it will match the default argument, and the specialization is taken. Otherwise, the primary template is taken.

    Edit: What the heck does the third parameter mean? Well, it's a default and it's 0. It will be passed when we name the specialization Foo<int, 5> for example. But really, we instantiate a template with the arguments Foo<int, 5, 0>, because the last is a default argument. The partial specialization matches, when the third parameter matches the third argument, which is zero by default, and if the third and second arguments are the same, because both are t.

    The above trick has the drawback that also Foo<int, 9, 9> uses our specialization. But on the other side, the above is remarkable simple, so that you can probably get away with that. If you don't want that to work, then you can use enable_if, which is a bit more complicated:

    template <typename T, T, typename = void> struct Foo {
    };
    
    template <typename T, T t> 
    struct Foo <T, t, typename boost::enable_if_c< t == 0 >::type> {
    };
    

    Now, even if you say Foo<int, 9, void>, our partial specialization won't be chosen, because the condition t == 0 isn't true, and ::type will thus not be available. SFINAE doesn't chose the specialization then. Of course, with this enable_if solution, you are not limited to t being zero. Any condition will do. For reference, here is the code of enable_if, if you don't use boost. Cut the _c suffix above then, which we don't need for the version below:

    template<bool C, typename T = void>
    struct enable_if {
      typedef T type;
    };
    
    template<typename T>
    struct enable_if<false, T> { };
    
    Hosam Aly : I am not sure I understand this. What is the third argument for? Could you please clarify?
    strager : Do you mean, for the second one: template struct ... ?
    Johannes Schaub - litb : edited the stuff.
    jpalecek : The third argument is there just to get a value 0 of type T in it. If you wanted to specialize for two values (like 0 and 1), you would have to use a type argument as described in the documentation of boost::enable_if, am I right?
    Johannes Schaub - litb : yeah, exactly. like the one here, the conditions must be disjunct, so that you don't get ambiguities: http://stackoverflow.com/questions/267418/elegant-template-specialization#275254
    j_random_hacker : Wow, nice trick litb!
  • @litb: Awesome trick, thanks.

    @Adam: I was trying to have the 'n' be of type T. Maybe I asked wrong, but litb's solution works great for me.

    Adam Rosenfield : What are you trying to do? Your example doesn't make a lot of sense.
    Daniel Earwicker : @Philly37 - if you like litb's answer, you would normally click the star icon to mark it as the right answer, rather than just replying with another "answer". :)

0 comments:

Post a Comment