SFINAE:这是怎么回事?

时间:2018-12-07 11:28:17

标签: c++

我目前正在尝试理解C ++代码,并且遇到了SFINAE构造(这对我来说是新的)。我基于下面的代码创建了一个最小的示例:

#include<iostream>

/* ----------------------------------------------
 Define two kernels: characterized by their dimension
   ---------------------------------------------- */
struct Kern2
{
  static constexpr int dim = 2;
};

struct Kern3
{
  static constexpr int dim = 3;
};

/* ----------------------------------------------
 Choose which function to evaluate based on 
 dimension of Kern (Kern::dim)
   ---------------------------------------------- */
template<class Kern,
         typename std::enable_if<Kern::dim == 2, bool>::type = true>
inline void apply_kern(){
  std::cout << "dim=2" << "\n";
}

template<class Kern,
         typename std::enable_if<Kern::dim == 3, bool>::type = false>
inline void apply_kern(){
  std::cout << "dim=3" << "\n";
}

// Try to see if the above SFINAE construct works!
int main()
{

 apply_kern<Kern2>(); // should print 'dim=2'
 apply_kern<Kern3>(); // should print 'dim=3'

  return 0;
}

这给出了输出:

> dim=2
> dim=3

这正是它应该做的。但是,我无法确切了解如何的工作原理?特别是,如果我切换

,似乎会创建相同的输出
typename std::enable_if<Kern::dim == 2, bool>::type = true

行到:

typename std::enable_if<Kern::dim == 2, bool>::type = false

所以我想知道这些是什么意思?如果有人可以解释发生的事情,我将不胜感激!不幸的是,我无法找到这种在线使用SFINAE的精确方法。

谢谢!

2 个答案:

答案 0 :(得分:15)

typename std::enable_if<Kern::dim == 2, bool>::type = true>

说:

typename:

以下术语定义类型

std::enable_if<Kern::dim == 2, bool>

如果第一个参数中的条件为true,则此模板定义第二个模板参数的类型。因此,在这里,如果dimm == 2为true,则模板std::enable_if提供了布尔类型,可以使用::type进行访问。

如果条件为真,则术语:

typename std::enable_if<Kern::dim == 3, bool>::type

变得简单:

bool

现在,您在其后添加= true。您是否在任何地方都使用了布尔值?没有!因此,这根本不重要!您还可以写:

typename std::enable_if<Kern::dim == 3, int>::type = 42

结果与您未使用此处定义的值相同!

您检查的条件在Kern::dim == 3中。这必须是对还是错。

如果条件的计算结果为false,则模板enable_if不包含type,并且表达式失败。 SFINAE在这里发挥作用。该失败不会是一个错误,但是会使模板定义“不可见”,因为它“不能”用作失败原因。

评论中添加问题的附加组件:

当然,您可以将名称添加到bool模板默认参数中,并在下面的代码中使用它,如下所示:

template<class Kern,
         typename std::enable_if<Kern::dim == 2, bool>::type myVal = true>
inline void apply_kern(){
  std::cout << "dim=2" << "\n";
  std::cout << "bool val: " << myVal << std::endl;
}

顺便说一句: 我们经常看到在简单的模板重载以相同方式工作的情况下使用SFINAE。通常,重载更容易阅读(这里可能不是:-))。我仅作为提示:检查是否确实需要SFINAE,并考虑过载。

模板重载而不是SFINAE:

/* ----------------------------------------------
   Define two kernels: characterized by their dimension
   ---------------------------------------------- */
struct Kern2 { static constexpr int dim = 2; };
struct Kern3 { static constexpr int dim = 3; };

/* ----------------------------------------------
   Choose which function to evaluate based on 
   dimension of Kern (Kern::dim)
   ---------------------------------------------- */
template < int x > inline void apply_kern_impl();

template<>
inline void apply_kern_impl<2>() { std::cout << "dim=2" << "\n"; }

template<>
inline void apply_kern_impl<3>() { std::cout << "dim=3" << "\n"; }

template< typename T>
inline void apply_kern() { apply_kern_impl<T::dim>(); }

int main()
{
    apply_kern<Kern2>(); // should print 'dim=2'
    apply_kern<Kern3>(); // should print 'dim=3'

    return 0;
}

答案 1 :(得分:1)

std::enable_if<bool expression, return type of function>::type在编译时通知编译器,如果bool表达式为true,则进行编译。因此,当您在apply_kern<Kern2>()中调用main()时,编译器将输入第一个std::enable_if,因为您的Kern :: dim确实为2。例如,如果您未调用apply_kern<Kern3>(),编译器会注意到第二个std::enable_if是错误的,并且其中的作用域不会被编译。这就像一个if语句,但是在编译时。您也可以分别为Kern2Kern3使用具有2个模板别名的模板化函数,如果发现这种语法很奇怪,则具有相同的精确结果。 对于最后一个问题中的相同结果,我尝试使用typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}