0

我目前正在移植我的库,但我的黑暗模板魔法不会用 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; };

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

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

将上面的模板片段重写为

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

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

另外,我不明白为什么我必须引入与 0 的比较。没有它,gcc 抱怨在 enabled_if 上缺少“类型”。

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

我可以重写它以返回 SFINAE 类型——但这将是很多我想避免的工作(区分函数、类、结构、typedefs / usings ...),即使它包含在宏中。

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

3 回答 3

1

这不是有效的 C++:

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

因为许多的原因。它使用了一个std不在std( is_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;
}

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

于 2016-05-11T18:10:27.640 回答
0

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

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

于 2016-05-11T13:04:27.493 回答
0

我会将 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存在。如果是这样,肯定不在 GCC 中。不要在 std 命名空间中注入东西,编译器可以自由地拒绝它。

于 2016-05-11T14:48:15.040 回答