Search code examples
c++templatesboostoverloadingenable-if

Specialize C++ member function based on class template argument


I have a class with a template parameter which should decide which of two styles of data it contains. Based on that parameter I want to implement a member function one of two different ways. I tried using Boost Enable-If, but without success. Here's the version of the code that I'm most surprised doesn't work:

#include <boost/utility/enable_if.hpp>
enum PadSide { Left, Right };
template <int> struct dummy { dummy(int) {} };

template <PadSide Pad>
struct String
{
    typename boost::enable_if_c<Pad ==  Left, void>::type
        getRange(dummy<0> = 0) {}
    typename boost::enable_if_c<Pad == Right, void>::type
        getRange(dummy<1> = 0) {}
};

int main()
{
    String<Left> field;
    field.getRange();
}

To this, g++ 4.6.0 says:

no type named ‘type’ in ‘struct boost::enable_if_c<false, void>’

Of course, the second overload is supposed to not work, but it's supposed to be ignored due to SFINAE. If I remove the dummy function parameters, g++ says this:

‘typename boost::enable_if_c<(Pad == Right), void>::type
    String<Pad>::getRange()‘
cannot be overloaded with
‘typename boost::enable_if_c<(Pad == Left), void>::type
    String<Pad>::getRange()‘

Which is why I put the dummy parameters there in the first place--following the Compiler Workarounds section of the documentation.

Basically what I want is to have two implementations of getRange(), and have one or the other be selected based on the Pad type. I was hoping that Enable-If would let me do it without making auxiliary classes to delegate the work to (which I'm going to try in the meantime).


Solution

  • Since you are going to be making two different versions of getRange() anyways, you can always overload your struct String member functions depending on the type of PadSide. I know it's not as "pretty", but in the end, it's still a similar amount of code, and you won't have to make multiple class types.

    template<PadSide Pad>
    struct String
    {
        void getRange();
    };
    
    template<>
    void String<Right>::getRange() { /*....*/ }
    
    template<>
    void String<Left>::getRange() { /*....*/ }