Class template SFINAE
cppreference sfinae # SFINAE in partial specializations
Deduction and substitution also occur while determining whether a specialization of a class or variable (since C++14) template is generated by some partial specialization or the primary template. Compilers do not treat a substitution failure as a hard-error during such determination, but ignore the corresponding partial specialization declaration instead, as if in the overload resolution involving function templates.
#include <type_traits>
#include <iostream>
// primary template handles non-referenceable types:
template<class T, class = void>
struct reference_traits
{
using add_lref = T;
using add_rref = T;
};
// specialization recognizes referenceable types:
template<class T>
struct reference_traits<T, std::void_t<T&>>
{
using add_lref = T&;
using add_rref = T&&;
};
template<class T>
using add_lvalue_reference_t = typename reference_traits<T>::add_lref;
template<class T>
using add_rvalue_reference_t = typename reference_traits<T>::add_rref;
int main()
{
std::cout << std::boolalpha << std::is_reference_v<add_lvalue_reference_t<void>> << std::endl; // false;
std::cout << std::boolalpha << std::is_reference_v<add_lvalue_reference_t<int>> << std::endl; // true;
}
// g++ --std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
NOTE:
1、输出如下:
false true
cpppatterns Class template SFINAE
#include <type_traits>
template <typename T, typename Enable = void>
class foo;
template <typename T>
class foo<T, typename std::enable_if<std::is_integral<T>::value>::type>
{ };
template <typename T>
class foo<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{ };
Requires
c++11 or newer.
NOTE: 需要c++11的原因是
type_traits
library是C++11中添加的
INTENT
Conditionally instantiate a class template depending on the template arguments.
DESCRIPTION
We provide two partial specializations of the foo
class template:
- The template on lines 6–8 will only be instantiated when
T
is an integral type. - The template on lines 10–12 will only be instantiated when
T
is a floating point type.
This allows us to provide different implementations of the foo
class depending on the template arguments it is instantiated with.
We have used std::enable_if
on line 7 and line 11 to force instantiation to succeed only for the appropriate template arguments. This relies on Substitution Failure Is Not An Error (SFINAE), which states that failing to instantiate a template with some particular template arguments does not result in an error and simply discards that instantiation.
NOTE: 如果对
std::enable_if
不熟悉,则可能会对上面这段话的解释产生质疑,我第一次就是如此。我以为enable_if
为第二个template parameterT
提供了default argumentvoid
,则在第一个template parameterB
的argument为false
的时候,则它提供的类型是void
,则上述代码会与第一个class template匹配。这是错误的。阅读std::enable_if
可知:If
B
is true, std::enable_if has a public member typedeftype
, equal toT
; otherwise, there is no member typedef.所以上述程序的实现所依赖的是SFINAE。
If you want to simply prevent a template from being instantiated for certain template arguments, consider using static_assert
instead.
Template specialization and enable_if problems [duplicate]
实现分析
1、SFINAE作用于argument of type template parameter。
2、它是基于partial template specialization
其他的实现模式
jni.hpp/include/jni/tagging.hpp
template<class Tag, class = int>
struct SuperTag
{
using Type = ObjectTag;
};
template<class Tag>
struct SuperTag<Tag, decltype(std::declval<typename Tag::SuperTag>(), 0)>
{
using Type = typename Tag::SuperTag;
};
ricab/scope_guard/scope_guard.hpp
// Type trait determining whether a type is callable with no arguments
template<typename T, typename = void>
struct is_noarg_callable_t
: public std::false_type
{}; // in general, false
template<typename T>
struct is_noarg_callable_t<T, decltype(std::declval<T&&>()())>
: public std::true_type
{}; // only true when call expression valid
Application
1、Class template SFINAE是实现trait的基础。
我的实践
/**
* 根据消息类型获得对应的序列化器、反序列化器 类型
*/
template<int ServiceMsgType, typename ReqFieldType, typename Enable = void>
struct MsgPackerTrait;
/**
* 根据消息类型获得对应的序列化器、反序列化器 类型
*/
template<int ServiceMsgType, typename RspFieldType, typename Enable = void>
struct MsgUnpackerTrait;
template<int ServiceMsgType, typename ReqFieldType>
struct MsgPackerTrait<ServiceMsgType, ReqFieldType, typename std::enable_if<IsTradeMsg(ServiceMsgType)>::type>
{
typedef CNoBizHeadMsgPacker<ReqFieldType> PackerType;
};
template<int ServiceMsgType, typename RspFieldType>
struct MsgUnpackerTrait<ServiceMsgType, RspFieldType, typename std::enable_if<IsTradeMsg(ServiceMsgType)>::type>
{
typedef CNoBizHeadMsgUnpacker<RspFieldType> UnpackerType;
};