自定义模板的 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);
}
这个例子工作得很好。选择了 float
的 pixel_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>
{};
返回的默认 type
是 T
,其中 std::is_enable_if
是 void
。
所以
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++ 库同质。
我的代码需要为 "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);
}
这个例子工作得很好。选择了 float
的 pixel_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>
{};
返回的默认 type
是 T
,其中 std::is_enable_if
是 void
。
所以
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++ 库同质。