自定义模板的 SFINAE 失败。需要明白为什么

SFINAE fails for custom template. Need to understand why

我的代码需要为 "validity" 测试各种像素类型。例如,如果浮点像素为 std::isnan() 报告 true,则浮点像素无效。

所以我有一个 "validator" 模板结构,专门用于我的各种像素类型(这里,仅用于 float)。我的代码使用全局模板函数通过 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);
}

这个例子工作得很好。选择了 floatpixel_validator 专业化。一切顺利。

但后来我尝试通过专门针对 float.

的自定义版本“std::enable_if”来减少模板表达式的冗长程度以使其清晰明了
template<class T, class VAL=T>
struct enable_if_floating
    : std::enable_if<std::is_floating_point<T>::value, VAL>
{};

所以现在不要写这个:

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

我会写

enable_if_floating<PEL>::value

...所以我的验证器变成:

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

不幸的是,当我更改我的“pixel_validator”以使用它时,代码无法构建。我的 enable_if_floating 不工作,所以 Visual Studio 找不到合适的专业化。那么我的输出就不足为奇了。

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 ==========

我的问题是,为什么?我的 enable_if_floating 怎么了?

注意:我什至将这段代码放在我的 main() 中,只是为了完整性检查。如果我的模板不好,我希望 static_assert() 失败,但事实并非如此。

// 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...");

另请注意:我的真实世界代码使用的谓词比这个简单的示例space节省了很多

不确定是唯一的问题,但确实是个问题。

如果你写

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

返回的默认 typeT,其中 std::is_enable_ifvoid

所以

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

成为,当PEL为浮点型时,

template<class PEL> // .....VVV   should be void, not PEL
struct pixel_validator<PEL, PEL>
{
    static bool validate(const PEL& p) { return !std::isnan(p); }
};

不符合 pixel_validator 声明(和主要定义)

template<class PEL, typename Enable=void>
struct pixel_validator
 { };

因为预期的第二种类型是 void,而不是 PEL

我看到两个可能的替代解决方案:或者您使用 void 作为 enable_is_floating 第二个模板参数的默认值

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

或者您使用 PEL 作为 pixel_validator 第二个模板参数的默认值

template <typename PEL, typename = PEL>
struct pixel_validator
 { };

我建议第一个与 std::enable_if 和标准 C++ 库同质。