user14717
user14717

Reputation: 5161

std::enable_if with std::is_same refuses to compile

The following code fails to compile

#include <type_traits>
#include <fftw3.h>

template<class Real>
class foo
{
public:
    foo(Real* x, size_t length,
        typename std::enable_if<std::is_same<double, Real>::value>::type* = nullptr)
    {
        y = fftw_alloc_real(length);
        fftw_plan plan = fftw_plan_r2r_1d(length, x, y, FFTW_REDFT10, FFTW_ESTIMATE);
        fftw_execute_r2r(plan, x, y);
        fftw_destroy_plan(plan);
    }

    foo(Real* x, size_t length,
        typename std::enable_if<std::is_same<float, Real>::value>::type* = nullptr)
    {
        y = fftwf_alloc_real(length);
        fftwf_plan plan = fftwf_plan_r2r_1d(length, x, y, FFTW_REDFT10, FFTW_ESTIMATE);
        fftwf_execute_r2r(plan, x, y);
        fftwf_destroy_plan(plan); 
    }


private:
    Real* y;
};

int main()
{
    std::vector<double> x{12, 83, 96.3};
    foo<double> fd(x.data(), x.length());

    std::vector<float> xf{12, 82, 96.2};
    foo<float> ff(x.data(), x.length());
}

giving error message

test.cpp:14:33: error: no type named 'type' in 'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
    typename std::enable_if<std::is_same<float, Real>::value>::type* = nullptr)

What is wrong with my syntax or understanding of std::enable_if that makes this not compile? What is the fix so that I can use a template which still calls non-templated code from FFTW?

Upvotes: 0

Views: 5988

Answers (1)

Justin
Justin

Reputation: 25297

Let's walk it through from the compiler's point of view. When you say foo<double>, you are instantiating the template foo with Real = double.

When we encounter the first constructor, we evaluate the enable_if, and find that typename std::enable_if<std::is_same<double, Real>::value>::type is void (since std::is_same<double, double>::value == true). All is fine.

When we encounter the second constructor, we again try to instantiate it by evaluating the enable_if. However, this time typename std::enable_if<std::is_same<float, Real>::value>::type does not exist, since std::is_same<float, double>::value == false. This is an error.


Any time you try to instantiate a foo, at least one of these two constructors will be ill-formed. If you wanted SFINAE to kick in, the constructors would need to be templated:

template<class Real>
class foo
{
public:
    template <typename T>
    foo(T x,
        typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr)
    {
        mantissa_bits = 52;
    }

    template <typename T>
    foo(T x,
        typename std::enable_if<std::is_same<float, T>::value>::type* = nullptr)
    {
        mantissa_bits = 23;
    }


private:
    size_t mantissa_bits;
};

Although I've normally seen SFINAE-capable constructors look like this:

    template <typename T, typename std::enable_if<std::is_same<double, T>::value, int>::type = 0>
    foo(T x)
    {
        mantissa_bits = 52;
    }

Upvotes: 3

Related Questions