我目前正在尝试理解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的精确方法。
谢谢!
答案 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语句,但是在编译时。您也可以分别为Kern2
和Kern3
使用具有2个模板别名的模板化函数,如果发现这种语法很奇怪,则具有相同的精确结果。
对于最后一个问题中的相同结果,我尝试使用typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}
。