我可以在所有 MSVC >= 2013 上安全地使用哪些 SFINAE 技巧?

Posted

技术标签:

【中文标题】我可以在所有 MSVC >= 2013 上安全地使用哪些 SFINAE 技巧?【英文标题】:What SFINAE tricks can I safely use on all MSVC >= 2013? 【发布时间】:2016-11-08 17:55:06 【问题描述】:

在 Qt 中,我有一个绝妙的想法(咳嗽,咳嗽),开始为标准库数据类型定义 qHash(用于 QHash 的散列函数,Qt 的关联容器之一)的重载:std::basic_stringstd::shared_ptr 和类似。

这个过程可能有一个捷径:不是追逐“任何可以用作 QHash 中的键的标准库类型”并为其添加 qHash 重载,我可以自动定义 qHash,如果type 有一个 std::hash 专门化它(合理地假设我们不想做比标准库在这个过程中所做的更多的事情)。

也就是说,我可以使用表达式 SFINAE 来实现类似的东西:

template<typename T>
auto qHash(const T &t) -> decltype(std::hash<T>()(t)) 
 
    return std::hash<T>()(t); 

不幸的是,尽管 Qt 需要 C++11 编译器,但表达式 SFINAE 不允许在任何地方因为 MSVC 不完全支持它(在撰写本文时:所有 MSVC 版本,最高和包括VS15 preview 5。反正Qt必须支持到2013年)。

因此,问题是:有没有办法做同样的事情,以某种方式

    不使用表达式 SFINAE 保证适用于所有 MSVC 版本 >= 2013?

我在想通过enable_if 之类的简单的 C++98 SFINAE 构造,但其他 SO 答案(如this one)让我认为 MSVC 2013 也可能错误编译,所以结果变成再次无法接受。

【问题讨论】:

blogs.msdn.microsoft.com/vcblog/2015/12/02/… 尝试标签调度。让您的qHash 标签调度“我可以std::hash&lt;T&gt;”到使用std::hash 或不使用std::hash 的实现。对您要测试的每一件事重复此操作。 我认为您可以在std::hash::result_type 上进行 SFINAE,根据定义,std::hash 的标准提供的专业化始终是std::size_t。即使使用原始编译器也应该可以工作。 blogs.msdn.microsoft.com/vcblog/2016/06/07/… @HansPassant:那是关于 MSVC 2015,而不是 2013。他们都没有声称支持 SFINAE 表达式,所以我们不能使用它。 :( 【参考方案1】:

我认为您不需要为此使用 SFINAE 表达式,按照这些思路应该可以工作。

template<typename T>
typename std::hash<T>::result_type qHash(const T &t) 
 
     return std::hash<T>()(t); 

或者几乎任何在hash::result_type 上执行 SFINAE 的方法。不幸的是,hash::result_type 在 C++17 中已弃用,但您仍然可以在 MSVC 2013 中使用 #ifdef 此代码。

【讨论】:

哈,当然,我正在阅读 N4594 并且发现 no 提到了std::hash&lt;T&gt;::result_type... @peppe Ta-Daa,我想你已经找到答案了 :)

以上是关于我可以在所有 MSVC >= 2013 上安全地使用哪些 SFINAE 技巧?的主要内容,如果未能解决你的问题,请参考以下文章

带有 Qt 5 和 Qt Creator 的 msvc2013

LNK2001 在 VS 2013 (MSVC 18) 上,但不在 VS 2015 (MSVC 19) 上

MSVC 导致静态 const 模板成员初始化失败

MSVC++ 2013 似乎允许对临时对象进行赋值,有效地将它们视为左值

与 GCC/MSVC 中的 lambda 转换构造函数的差异

使用 MSVC C++ 编译器创建动态大小的数组