rlb*_*ond 113 c++ templates metaprogramming sfinae
我想进入更多的模板元编程.我知道SFINAE代表"替换失败不是错误".但是有人能告诉我SFINAE的用处吗?
Joh*_*itb 87
我喜欢SFINAE用来检查布尔条件.
template<int I> void div(char(*)[I % 2 == 0] = 0) {
/* this is taken when I is even */
}
template<int I> void div(char(*)[I % 2 == 1] = 0) {
/* this is taken when I is odd */
}
Run Code Online (Sandbox Code Playgroud)
它非常有用.例如,我用它来检查使用运算符逗号收集的初始化列表是否不长于固定大小
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)
仅当M小于N时才接受该列表,这意味着初始化列表没有太多元素.
语法char(*)[C]表示:指向元素类型为char和size的数组的指针C.如果C为false(这里为0),那么我们得到无效类型char(*)[0],指向零大小数组的指针:SFINAE使得模板将被忽略.
用表示boost::enable_if,看起来像这样
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i,
typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)
在实践中,我经常发现能够检查条件是否有用.
Gre*_*ers 67
下面是一个例子(从这里开始):
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
Run Code Online (Sandbox Code Playgroud)
在IsClassT<int>::Yes计算时,0无法转换为int int::*因为int不是类,所以它不能有成员指针.如果SFINAE不存在,那么你会得到一个编译器错误,类似'0无法转换为非类型int的成员指针'.相反,它只使用...返回Two 的表单,因此计算结果为false,int不是类类型.
odi*_*erd 13
在C++ 11中,SFINAE测试变得更加漂亮.以下是常见用途的几个示例:
根据特征选择函数重载
template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
//integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
//floating point version
}
Run Code Online (Sandbox Code Playgroud)
使用所谓的类型接收器习惯用法,您可以对类型执行相当任意的测试,例如检查它是否具有成员以及该成员是否属于某种类型
//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;
//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};
struct S{
int bar;
};
struct K{
};
template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
std::cout << "has bar" << std::endl;
}
void print(...){
std::cout << "no bar" << std::endl;
}
int main(){
print(S{});
print(K{});
std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
这是一个现场的例子:http://ideone.com/dHhyHE 我最近也在我的博客上写了关于SFINAE和标签发送的整个部分(无耻插件但相关)http://metaporky.blogspot.de/2014/08/部分7-静态调度,function.html
注意,从C++ 14开始,有一个std :: void_t,它与我的TypeSink基本相同.
小智 7
在我看来,其他答案提供的示例比需要的更复杂。
这是cppreference中稍微容易理解的示例:
#include <iostream>
// this overload is always in the set of overloads
// ellipsis parameter has the lowest ranking for overload resolution
void test(...)
{
std::cout << "Catch-all overload called\n";
}
// this overload is added to the set of overloads if
// C is a reference-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)(c.*f)(), void())
{
std::cout << "Reference overload called\n";
}
// this overload is added to the set of overloads if
// C is a pointer-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)((c->*f)()), void())
{
std::cout << "Pointer overload called\n";
}
struct X { void f() {} };
int main(){
X x;
test( x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
Run Code Online (Sandbox Code Playgroud)
输出:
Reference overload called
Pointer overload called
Catch-all overload called
Run Code Online (Sandbox Code Playgroud)
如您所见,在第三次调用 test 时,替换失败且没有错误。
这是另一个(已故的)SFINAE示例,基于Greg Rogers的回答:
template<typename T>
class IsClassT {
template<typename C> static bool test(int C::*) {return true;}
template<typename C> static bool test(...) {return false;}
public:
static bool value;
};
template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以检查 的value值以查看是否T是一个类:
int main(void) {
std::cout << IsClassT<std::string>::value << std::endl; // true
std::cout << IsClassT<int>::value << std::endl; // false
return 0;
}
Run Code Online (Sandbox Code Playgroud)