Partial specialization of a method in a templated class
Asked Answered
G

4

25

Given:

struct A
{
    virtual bool what() = 0;
};

template<typename T, typename Q>
struct B : public A
{
    virtual bool what();
};

I want to partially specialize what like:

template<typename T, typename Q>
bool B<T, Q>::what()
{
    return true;
}

template<typename Q>
bool B<float, Q>::what()
{
    return false;
}

But it appears that this isn't possible (is it in C++11?) so I tried SFINAE:

template<typename T>
typename std::enable_if<std::is_same<T, float>::value, bool>::type B<T>::what()
{
    return true;
}

template<typename T>
typename std::enable_if<!std::is_same<T, float>::value, bool>::type B<T>::what()
{
    return false;
}

This also doesn't work, I have no idea why though, does anyone? So I found this thread and ended up with:

template<typename T, typename Q>
struct B : public A
{
    virtual bool what()
    {
        return whatimpl(std::is_same<T, float>());
    }

    bool whatimpl(std::false_type)
    {
        return false;
    }

    bool whatimpl(std::true_type)
    {
        return true;
    }
};

This final solution works, but why doesn't the enable_if technique work? I'm also very open to suggestions of a cleaner answer that I haven't encountered yet.

I simplified my examples as much as possible - in my real use case what() isn't called what and actually does a fair bit of work, and I'll want to 'specialize' on a user defined type, not float.

Greenleaf answered 23/4, 2012 at 16:31 Comment(4)
@Nawaz I did realize that, but this is just a simplified case to show what I'm trying to do :) Read the last line in the post.Greenleaf
what isn't a template method, to override A::what() it should be a single non-template method of class template B. You can't specialize non-template method, neither with enable_if nor with any other techique. However you can specialize the whole class BSpreader
@Spreader Then why does this work (full specialization instead of partial): template<> bool B<float, float>::what() { return false; }Greenleaf
Interesting indeed (BTW +1 for the question) seems I'm wrong. I guess the reason this not working is that partial specialization is not allowed for functions at all (as the standard says).Spreader
S
13

Partial specialization is explicitly permitted by the standard only for class templates (see 14.5.5 Class template partial specializations)

For members of class template only explicit specialization is allowed.

14.7 (3) says:

An explicit specialization may be declared for a function template, a class template, a member of a class template or a member template. An explicit specialization declaration is introduced by template<>.

So any definition starting with

template<typename T>  

is not an allowed syntax for member of class template specialization.

[edit]

As to SFINAE attempt, it failed because actually there are neither overloads nor specializations here (SFINAE works while defining a set of candidate functions for overload resolution or while choosing proper specialization). what() is declared as a single method of class template and should have a single definition, and this definition should have a form:

template<typename T, typename Q> 
B<T,Q>:: bool what(){...}

or may be also explicitly specialized for particular instantiation of class B:

template<> 
B<SomeParticularTypeT,SomeParticularTypeTypeQ>:: bool what(){...}

Any other forms are syntacticaly invalid, so SFINAE can't help.

Spreader answered 24/4, 2012 at 8:2 Comment(6)
Thanks, this explains the first failed attempt. Any insight on the SFINAE attempt?Greenleaf
@Dave: SFINAE abbreviates "SPECIALIZATION failure is not an error", so it is just a particular technique for specialization. Starting with template<typename T>... you tried to partially specialize a method of class template just using some more complicated way.Spreader
@Dave:: ...or template<typename T> may start method definition for class template B<T>, but in this case it is not a specialization at all and SFINAE also doesn't work.Spreader
@Dave: sorry... SFINAE means "substitution.."; nevertheless all the above seems right :).Spreader
sorry again... something wrong in my comments :) I've edited the answerSpreader
Anyone who can explain why partial specialization isn't allowed?Pinch
X
2

Why not just change it to..

template<typename T, typename Q> 
struct B : public A 
{   
   bool what()
   {
      return false; //Or whatever the default is...
   }
}; 

template<typename Q>
struct B<float, Q> : public A 
{   
   bool what()
   {
      return true;
   }
}; 
Xeniaxeno answered 23/4, 2012 at 20:1 Comment(5)
Because B is a giant class and I don't want to copy/paste a giant class 15 times for each specialization of what()Greenleaf
@Dave: ... although you may inherit from intermediate giant class adding only what() override in the final class.Spreader
@Spreader I could only do that if I added an extra class in the inheritance - note that A isn't templated so B's functionality really couldn't be put in there. The extra class would need to be templated and go between A and B. That could be written as another answer to respond to other techniques to handle thisGreenleaf
@Dave: Indeed, I actually meant to introduce additional (intermediate "giant") class template between A and B.Spreader
You never said anything about B being a giant class, based off your example and description alone I proposed this solution. Thanks for the downvote.Xeniaxeno
H
0

Two possible solutions depending on your use case:

  1. Flexible implementation classes: One problem with your method is that there is a lack of flexibility in the types - no more than true or false type. Based on this excellent CppCon talk(Slide 77), I wrote up this solution by delegating the work to another implementation specific class template. You can see and run this code here. The drawback to this method is that I can't access the rest of the class members but they can be passed in.
  2. Manipulating Enable If: I haven't fully understood why your enable_if solution didnt work but here is mine and it is working. It allows you to partially specialize within the class itself.

P.S. I'm trying to add the code here directly but there is some formatting problem. If someone could help me out with adding the formatted code from Coliru it'd be great.

Horton answered 18/9, 2018 at 16:50 Comment(0)
A
0

You can use static function an templatize it, like this:

#include <iostream>
using namespace std;

struct A
{
    virtual bool what() = 0;
};

template <typename T, typename Q>
static bool what()
{
    cout << "T: " << typeid (T).name() << " Q: " << typeid (Q).name() << endl;
    return sizeof (T) < sizeof (Q);
}

template <typename Q>
static bool what(float t, Q q)
{
    cout << "T: " << t << " Q: " << q << endl;
}

template<typename T, typename Q>
struct B : public A
{
    virtual bool what()
    {
        return ::what<T, Q>();
    }
};

int main()
{
    B<int, char> bic;
    B<float, char> bfc;

    cout << bic.what() << endl;
    cout << bfc.what() << endl;

    return 0;
}
Anthropometry answered 28/10, 2023 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.