【问题标题】:SFINAE fails for custom template. Need to understand why自定义模板的 SFINAE 失败。需要了解原因
【发布时间】:2019-06-29 23:47:48
【问题描述】:

我的代码需要测试各种像素类型的“有效性”。例如,如果浮点像素为std::isnan() 报告true,则它们是无效的。

所以我有一个“验证器”模板结构,我专门针对我的各种像素类型(这里只针对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...");

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

【问题讨论】:

  • enable_if_floating的定义中std::enable_if的第二个参数是不必要的,看来
  • 这个想法是使用它,就像在 enable_if 中使用第二个参数一样;也就是说,有时我希望它评估的类型(当它被选择时)不同于提供的类型 T
  • 明确地说,我发布这个问题不是为了寻找解决方法,我发布它是为了了解为什么这种方法不起作用

标签: c++11 templates template-meta-programming sfinae visual-studio-2019


【解决方案1】:

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

如果你写

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++ 库同质化。

【讨论】:

  • 根据文档的第二个参数 enable_if 只是告诉它实例化的类型如果布尔为真。但如果 bool 为 false,则不会实例化任何类型,SFINAE 仍应正常工作。不应该吗? docs.microsoft.com/en-us/cpp/standard-library/…
  • @Joe - 完全正确。但是当结构的主版本期望(默认值)void 作为第二个模板参数时,特化应该有一个void 作为第二个模板参数。如果是PEL,则不匹配所以选择主版本。
  • 天哪,当然! (摇头)既然你已经指出了这一点,我觉得几小时前没有意识到这一点很愚蠢。即使经过 5 年的模板元处理,我的思绪仍会重新回到它的老式 C++ 方式;我将其视为重载函数而不是类型谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-05-05
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
  • 1970-01-01
  • 2020-12-22
  • 2012-10-01
相关资源
最近更新 更多