Reputation: 6806
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 "std::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
Upvotes: 1
Views: 180
Reputation: 66210
Not sure is the only problem but is a problem.
If you write
template<class T, class VAL=T>
struct enable_if_floating
: std::enable_if<std::is_floating_point<T>::value, VAL>
{};
the default returned type
is T
where, for std::is_enable_if
, is void
.
So
template<class PEL>
struct pixel_validator<PEL, typename enable_if_floating<PEL>::type>
{
static bool validate(const PEL& p) { return !std::isnan(p); }
};
become, when PEL
is a floating point type,
template<class PEL> // .....VVV should be void, not PEL
struct pixel_validator<PEL, PEL>
{
static bool validate(const PEL& p) { return !std::isnan(p); }
};
that doesn't matches the pixel_validator
declaration (and main definition)
template<class PEL, typename Enable=void>
struct pixel_validator
{ };
because the expected second type is void
, not PEL
.
I see two possible alternative solutions: or you use void
as default value for enable_is_floating
second template parameter
// ...........................VVVV
template<class T, class VAL = void>
struct enable_if_floating
: std::enable_if<std::is_floating_point<T>::value, VAL>
{ };
or you use PEL
as default value for pixel_validator
second template parameter
template <typename PEL, typename = PEL>
struct pixel_validator
{ };
I suggest the first one to homogeneity with std::enable_if
and standard C++ library.
Upvotes: 2