mardi 17 avril 2018

Use a enable_if that does not depend on method's template parameter

I'm trying to use std::enable_if and SFINAE to switch out the implementation of a class template's method based purely on the template parameters of the class. Example:

#include <type_traits>

template<class T1, class T2>
class Foo {
    template<class InnerT, class ... Args>
    typename std::enable_if<std::is_same<T1, T2>::value, void>::type
    bar(InnerT param) {};

    template<class InnerT, class ... Args>
    typename std::enable_if<!std::is_same<T1, T2>::value, void>::type
    bar(InnerT param) {};
};


int main() {
    Foo<int, int> f;
}

Here, bar should behave differently based on whether T1 and T2 are the same type or not. However, this code does not compile. Neither GCC nor clang tell me anything useful. I suspect the problem is that the enable_if condition does not depend on the parameters of bar, i.e., not on its immediate context as specified in paragraph 17.8.2, point 8, of the standard.

This assumption is supported by the fact that this code compiles fine:

#include <type_traits>

class DummyClass {};

template<class T1, class T2>
class Foo {
    template<class InnerT, class ... Args>
    typename std::enable_if<std::is_same<T1, T2>::value || 
                            std::is_same<InnerT, DummyClass>::value, void>::type
    bar(InnerT param) {};

    template<class InnerT, class ... Args>
    typename std::enable_if<!std::is_same<T1, T2>::value || 
                            std::is_same<InnerT, DummyClass>::value, void>::type
    bar(InnerT param) {};
};


int main() {
    Foo<int, int> f;
}

Now the expression inside the enable_if depends on the "immediate context", namely InnerT, even though that part of the expression always evaluates to false.

It looks like I can use this as a workaround, but that feels really hacky and ugly. How do you solve this problem "correctly"? A thought I had was to add an additional template parameter (call it DummyType) to bar, which defaults to e.g. DummyType = T1, and then check std::is_same<DummyType, T2>, but the fact that bar takes a parameter pack makes this impossible (or does it…?)

Aucun commentaire:

Enregistrer un commentaire