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

时间:2016-05-11 12:42:03

标签: c++ visual-studio-2015 c++14 porting gcc5

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

使用MSVC2015 Update 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; };

海湾合作委员会抱怨“目前范围内没有定义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类型 - 但这将是我要避免的很多工作(区分函数,类,结构,typedef / usings ......),即使它包含在宏中

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

3 个答案:

答案 0 :(得分:1)

这不是有效的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之前的vreal

我不知道它应该是什么意思,除了你似乎认为它具有SFINAE魔力:它表明如果is_vreal通过floatType测试,它应该是类型{{ 1}}默认情况下。但是为了达到这一点,您必须已经使用类型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;
}

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

答案 1 :(得分:0)

对于您的顶级功能,您可以拥有:

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

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

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在某处适当定义)

答案 2 :(得分:0)

我会将SFINAE与您实际使用的模板参数分开。对于这种情况,非类型的附加参数是理想的。

对于struct,我会使用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命名空间中注入内容,编译器可以自由拒绝它。