📅  最后修改于: 2023-12-03 15:13:54.926000             🧑  作者: Mango
在C++中,SFINAE (Substitution Failure Is Not An Error) 是一种技术,可以帮助我们实现部分特化和重载函数,在编译器构建模板实例时排除某些类型,并且仍然保持编译过程的平稳。
在这种情况下,编译器并不认为模板出现了错误,而只是“在候选模板中差异很小”,因此模板被“剔除”在外,候选模板的匹配将会考虑其他可用模板。
SFINAE 是一个术语,用于描述编译器模板解析行为的一部分。通常,如果模板无法合法地应用于某些类型,则使用SFINAE覆盖异常处理机制,从而使编译器可以将该模板排除在匹配策略外,从而尝试其他备选模板。
在C++中,宏替换和命名约定已经足够灵活,可以轻松地处理许多相对简单的问题。但随着函数和类的数目不断增加,程序员不得不更多地依赖于超载技术和各种其他技术。这导致一些问题,例如重载决策,部分特化等等。
SFINAE的优点是,在编译时排除不适用于特定类或类型的功能。这使代码更清晰,更易于维护,并且可以使代码更加高效。
一个简单的例子管有一个类 check_type
template<typename T>
class check_type
{
struct yes {char c;};
struct no {char c[2];};
template<typename C>
static yes test(typename C::type*);
template<typename C>
static no test(...);
public:
static const bool value = (sizeof(test<T>(0)) == sizeof(yes));
};
上面代码中, struct yes
和 struct no
分别提出两种类型, 然后提出了两种函数 test()
, 如果一个类型定义了 type
,那么 那么 test()
获取 C的类型经过提取之后是 yes,否则它就是 no 。
这样在定义 check_type
类时就可以测试那个其中某个对象可以访问 type ,具体做法如下:
class A {public: typedef int type;};
class B {};
// Check if A and B have type members
int main()
{
std::cout << std::boolalpha;
std::cout << check_type<A>::value << std::endl; // true
std::cout << check_type<B>::value << std::endl; // false
return 0;
}
一个常见的用例是通过 SFINAE 剔除一些过期功能,以保持代码更加清晰和密集。下面的例子说明了我们如何使用SFINAE和std :: enable_if的联合使用来使编译器选择特定的函数,即使该函数的类型参数不完全匹配。
#include <type_traits>
template<typename T>
typename std::enable_if<!std::is_integral<T>::value, void>::type
foo(T t)
{
//...
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
foo(T t)
{
//...
}
上面的例子中,我们定义了两个函数 foo
,它们不完全具有相同的参数类型。因为这些模板都具有可转换的模板参数,并且因为 enable_if
通过向函数稍微添加一些优势来构成更复杂的功能,这些模板可以特化为非常精细的方式。
template< class T >
typename std::enable_if<
std::is_integral<T>::value && sizeof(T) < 4,
T
>::type
increment(T t)
{
return (T)(++t);
}
template< class T>
typename std::enable_if<
std::is_floating_point<T>::value,
T
>::type
increment(T t)
{
return ++t;
}
上面的例子中, 我们定义了两个函数 increment()
,在 std::is_integral
类型中,它仅作用于大小超过4的类型,而对于浮点型来说,则没有这种限制。
SFINAE可以在编译器构建模板实例时排除某些类型,并且仍然保持编译过程的平缓。这使得其在设计C ++ 模板时非常有用。但是,SFINAE还有一些局限性值得注意:
程序员需要熟悉C ++ 模板机制,这些机制常常比常规C ++ 代码更加复杂和晦涩。
SFINAE不适合解决所有问题。当出现复杂条件检查和部分特化时,SFINAE往往不能提供足够的支持。
SFINAE代码通常很难阅读和理解,这是因为其复杂性和晦涩,因此程序员需要谨慎地使用它。
虽然某些C ++ Only-features看起来很复杂,但它们通常可以帮助我们解决更复杂的问题,并保持我们的代码整洁和简单。SFINAE既是其中一种,它使我们能够编写C ++ 模板,这些模板可以轻松地处理类型驱动的情况,并且为我们提供了更高效的代码和设计能力。在程序员了解了SFINAE的原理和代码的局限性之后,它应该成为程序员组成C ++ 工具箱的一个有力工具之一。