Search code examples
c++sfinaeenable-if

SFINAE Constructors


I have been liking SFINAE syntax like this for functions, seems to generally work well!

template <class Integer, class = typename std::enable_if<std::is_integral<Integer>::value>::type>
T(Integer n) {
    // ...
}

But am running into an issue when I want to do this as well in the same class...

template <class Float, class = typename std::enable_if<std::is_floating_point<Float>::value>::type>
T(Float n) {
    // ...
}

Getting errors such as this:

./../T.h:286:2: error: constructor cannot be redeclared
        T(Float n) {
        ^
./../T.h:281:2: note: previous definition is here
        T(Integer n) {
        ^
1 error generated.

Shouldn't these constructors only exist for the appropriate types and never at the same time? Why do they conflict?

Am I being a bit thick here?

This on the other hand does work (but I don't like the syntax as much):

template <class Integer>
T(Integer n, typename std::enable_if<std::is_integral<Integer>::value>::type* = nullptr) {
}

template <class Float>
T(Float n, typename std::enable_if<std::is_floating_point<Float>::value>::type* = nullptr) {
}

Solution

  • Use a non-type template parameter instead:

    template <class Integer,
        std::enable_if_t<std::is_integral<Integer>::value, int> = 0>
    T(Integer n) {
        // ...
    }
    
    template <class Float,
        std::enable_if_t<std::is_floating_point<Float>::value, int> = 0>
    T(Float n) {
        // ...
    }
    

    This works because the compiler has to substitute the first template parameter before it can determine the type of the value parameter.