samedi 29 juin 2019

SFINAE fails for custom template. Need to understand why

My code needs to test various pixel types for "validity". For example, floating point pixels are invalid if they report true for std::isnan().

So I have a "validator" template struct that I specialize for my various pixel types (here, just for float). My code uses a global template function to invoke the right overload through SFINAE

// Dummy implementation breaks compilation if no overload found.
template<class PEL, typename Enable=void> struct pixel_validator  { };


template<class PEL> 
struct pixel_validator<PEL, typename std::enable_if<std::is_floating_point<PEL>::value>::type>
{
    static bool validate(const PEL& p) { return !std::isnan(p);  }
};


template<class PEL>
inline bool is_valid_pixel(const PEL& p) 
{
    // Dispatch to validator above
    return pixel_validator<PEL>::validate(p); 
};


void main
{
     float x = 1.0f;
     std::cout << "is it valid ?" << std::boolalpha << is_valid_pixel(x);
}

And this example works just fine. The pixel_validator specialization for float is chosen. All is well.

But then I tried to reduce the verbosity of my template expressions for clarity via a custom version of "enable_if" specifically for float.

template<class T, class VAL=T>
struct enable_if_floating
    : std::enable_if<std::is_floating_point<T>::value, VAL>
{};

So now instead of writing this:

std::enable_if<std::is_floating_point<PEL>::value>::type

I can write

enable_if_floating<PEL>::value

... so my validator becomes:

template<class PEL> 
struct pixel_validator<PEL, typename enable_if_floating<PEL>::type>
{
    static bool validate(const PEL& p) { return !std::isnan(p); }
};

Unfortunately, the moment that I change my "pixel_validator" to use it, the code fails to build. My enable_if_floating does not work and so Visual Studio cannot find the appropriate specialization. My output is not surprising then.

1>------ Build started: Project: TestApp7, Configuration: Debug Win32 ------
1>TestApp7.cpp
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:  'validate': is not a member of 'pixel_validator<PEL,void>'
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:         with
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:         [
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:             PEL=float
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:         ]
1>C:\Test\TestApp7\TestApp7.cpp(62): message :  see declaration of 'pixel_validator<PEL,void>'
1>C:\Test\TestApp7\TestApp7.cpp(62): message :         with
1>C:\Test\TestApp7\TestApp7.cpp(62): message :         [
1>C:\Test\TestApp7\TestApp7.cpp(62): message :             PEL=float
1>C:\Test\TestApp7\TestApp7.cpp(62): message :         ]
1>C:\Test\TestApp7\TestApp7.cpp(82): message :  see reference to function template instantiation 'bool is_valid_pixel<float>(const PEL &)' being compiled
1>C:\Test\TestApp7\TestApp7.cpp(82): message :         with
1>C:\Test\TestApp7\TestApp7.cpp(82): message :         [
1>C:\Test\TestApp7\TestApp7.cpp(82): message :             PEL=float
1>C:\Test\TestApp7\TestApp7.cpp(82): message :         ]
1>C:\Test\TestApp7\TestApp7.cpp(62,1): error C3861:  'validate': identifier not found
1>Done building project "TestApp7.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

My question is, why? What is wrong with my enable_if_floating?

Note: I even put this code in my main, just for sanity checking. If my template were bad, I would expect the static_assert to fail, but it does not.

// Sanity check #2.  Does my enable_if_floating test  reports that float
// enables because it's numeric?  If not then the static_assert below should fail

using float_type = enable_if_floating<float>::type;
static_assert(std::is_same_v<float_type, float>, "Not same as float...");

Note also: My real world code uses a predicate that saves a whole lot more space than in this simple example

Aucun commentaire:

Enregistrer un commentaire