从MSVC2015更新2移植到GCC 5.3-SFINAE错误

Porting from MSVC2015 Update 2 to GCC 5.3 - SFINAE Errors

本文关键字:GCC 3-SFINAE 错误 MSVC2015 更新      更新时间:2023-10-16

我目前正在移植我的库,但我的黑暗模板魔法不会用GCC 5.3 编译

当使用MSVC2015更新2编译时,此片段可以正常工作

template<typename vreal = 
    std::enable_if<std::is_vreal<vreal>::value, 
                   floatType>::type>
inline vreal foo(vreal bar)
{
    return bar;  
}
template<typename vreal = 
    std::enable_if<std::is_vreal<vreal>::value, 
                   floatType>::type>
struct bar { vreal i; };

GCC抱怨"vreal未在当前范围内定义(WTF?)"

到目前为止,我已经尝试过:

将上层模板片段重写为

template<typename vreal, 
         typename enable = typename std::enable_if<std::is_vreal<vreal>::value != 0>::type>

但这也不起作用。它在代码的后面中断了很多,我认为这是由于引入了额外的模板参数。

此外,我不明白为什么我必须介绍与0的比较。如果没有它,gcc抱怨enabled_if上缺少"type"。

因此,主要问题是:如何在没有额外参数的情况下获得相同的SFINAE逻辑(仅当参数为vreal时编译)。

我可以重写它以返回类型SFINAE,但这将是我想避免的大量工作(区分函数、类、结构、typedefs/usings…),即使它被封装在宏中。

 template<typename vreal>
 typename std::enable_if<is_vreal<vreal>, vreal>::type inline vreal .....

这不是有效的C++:

template<typename vreal = 
  std::enable_if<std::is_vreal<vreal>::value, 
               floatType>::type>
inline vreal foo(vreal bar)
{
  return bar;  
}

原因有很多。它在std中使用了一个不在stdis_vreal)中的符号,这意味着您的程序格式不正确。它使用未定义的令牌floatType(您确实发布了[MCVE],对吗?)。在vreal的定义中,它在进入范围之前使用vreal

我不知道它应该是什么意思,除了你似乎相信它有SFINAE的魔力:它指出,如果vreal通过了is_vreal测试,那么默认情况下它应该是floatType类型。但为了达到这一点,您必须已经拥有类型vreal,所以默认类型似乎无关紧要。

此外,::type不是依赖上下文中的类型:,因此std::enable_if<std::is_vreal<vreal>::value, floatType>::type应该抱怨您在需要类型的上下文中使用了名为::type的非类型。你需要做typenam estd::enable_if<std::is_vreal<vreal>::value, floatType>::type

您似乎还声明您正在使用宏来生成代码。那是一个糟糕的决定。

据我所知,在大多数情况下,只需完全删除enable_if子句就可以解决您的问题。

函数除外,因为可能存在重载,所以可以引入SFINAE帮助程序。

完全放弃宏。无论如何,类和函数模板的工作方式明显不同。

对于类/结构:

template<class vreal>
struct bar { vreal i; };

因为真的没有其他选择了——没有像函数那样用结构/类重载的概念。

对于函数,我们想要一个SFINAE测试,这样我们就可以过载:

template<class T, class R=T>
using vreal_test = typename
  std::enable_if<std::is_vreal<T>::value, R>::type
template<class vreal>
inline vreal_test<vreal> foo(vreal bar)
{
  return bar;  
}

如果函数返回不同的类型,则执行

template<class vreal>
inline vreal_test<vreal,void> foo2(vreal bar)
{
  return;
}

您可以在扫描以删除宏时执行此操作。

对于您的顶级函数,您可以拥有以下内容:

template<typename vreal>
inline typename std::enable_if<std::is_vreal<vreal>::value, floatType>::type foo(vreal bar)
{
    return bar;
}

您的结构也应该重新定义。下面的解决方案无法摆脱在这里使用辅助参数Enable,具有部分专业化:

template<typename vreal, typename Enable = void>
struct bar;
template<typename vreal>
struct bar<vreal, typename std::enable_if<std::is_vreal<vreal>::value, void>::type>
{ vreal i; };

(假设floatType和std::is_vreal在某个地方得到了适当的定义)

我会将SFINAE与您实际使用的模板参数分开。非类型的附加参数非常适合这种情况。

对于结构,我将使用enable_if并专门化一个基类。

它看起来是这样的:

template<typename vreal, typename std::enable_if<std::is_vreal<vreal>::value, int>::type = 0>
vreal foo(vreal bar) { 
    return bar;
}
template<typename vreal, typename = void>
struct barBase; // optionally add a static_assert for better messages
template<typename vreal>
struct barBase<vreal, typename std::enable_if<std::is_vreal<vreal>::value>::type> {
    vreal i;
};
template<typename vreal>
using bar = barBase<vreal>;

我认为std::is_vreal不存在。如果是这样的话,海湾合作委员会肯定不会。不要在std命名空间中注入东西,编译器可以自由拒绝。