4

I can't understand why the following code is not working. compiler (gcc) seems to instanciate both methods and obviously integer is either signed or unsigned, so one always fails. I though enable_if was here to avoid that.

Q: why the compile error, and how to avoid it ?

using namespace boost; // or std as you want

template<typename T>
struct test {
    // if signed
    template< typename enable_if
                        < is_signed<T>
                        , int
                        >:: type = 0
            >
    test &operator<<=(int value)
    {}

    // if unsigned
    template< typename enable_if
                        < is_unsigned<T>
                        , int
                        >:: type = 0
            > 
    test &operator<<=(int value)
    {}
};

void foo()
{
    test<int> x;
    x << 1;            // COMPILE ERROR no type named 'type' in struct enable_if<unsigned> etc.
    test<unsigned> y;
    y << 1;            // COMPILE ERROR no type named 'type' in struct enable_if<int> etc.
}
syp
  • 61
  • 2
  • 2
    Define "it's not working". What happens? How does it differ from what you wanted to happen? We cannot magically divine your intention solely from a piece of code that, by your own admission, _does not represent that goal_. โ€“ Lightness Races in Orbit Dec 08 '14 at 14:35
  • added... but redundant with 'seems to implement both methods...' โ€“ syp Dec 08 '14 at 22:52
  • Not "redundant" at all, no. We are scientists. We value _precision_ and _specifics_ and _data_. "Seems to implement both methods" is none of those things!! โ€“ Lightness Races in Orbit Dec 08 '14 at 23:06

1 Answers1

5

SFINAE is applicable only in an immediate context (SFINAE = SFIICINAE, Substitution Failure In Immediate Context Is Not An Error):

ยง14.8.2 [temp.deduct]/p8:

Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.

A substitution of class template parameters in a member function's declaration is not an immediate context, and results in hard error on failure.

You can either partially-specialize entire class depending on the type, or introduce an immediate context yourself by adding a template parameters to the member function template itself, so that a substitution failure can result in soft error:

template <typename U = T, typename std::enable_if<std::is_signed<U>{}, int>::type = 0>
test &operator<<=(int value)
{ return *this; }

template <typename U = T, typename std::enable_if<std::is_unsigned<U>{}, int>::type = 0>
test &operator<<=(int value)
{ return *this; }
Community
  • 1
  • 1
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160