首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ SFINAE示例?

C++ SFINAE示例?
EN

Stack Overflow用户
提问于 2009-06-11 18:25:59
回答 9查看 55.9K关注 0票数 152

我想了解更多的模板元编程。我知道SFINAE代表“替换失败不是一个错误”。但是有人能给我展示一下SFINAE的一个好用法吗?

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2009-06-11 18:54:42

这里有一个例子(from here):

代码语言:javascript
复制
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 };
};

在计算IsClassT<int>::Yes时,0不能转换为int int::*,因为它不是一个类,因此它不能有成员指针。如果SFINAE不存在,那么你会得到一个编译器错误,比如'0不能被转换成非类类型int的成员指针‘。相反,它只使用返回2的...表单,因此计算结果为false,int不是类类型。

票数 79
EN

Stack Overflow用户

发布于 2009-06-12 23:40:28

我喜欢使用SFINAE检查布尔条件。

代码语言:javascript
复制
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 */
}

它可能非常有用。例如,我使用它来检查使用操作符逗号收集的初始化器列表是否不超过固定大小

代码语言:javascript
复制
template<int N>
struct Vector {
    template<int M> 
    Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}

只有当M小于N时,该列表才被接受,这意味着初始化器列表没有太多的元素。

语法char(*)[C]的意思是:指向元素类型为char、大小为C的数组的指针。如果C是false (这里是0),那么我们得到无效类型char(*)[0],指向一个零大小数组的指针: SFINAE会这样做,这样模板就会被忽略。

boost::enable_if表示,看起来像这样

代码语言:javascript
复制
template<int N>
struct Vector {
    template<int M> 
    Vector(MyInitList<M> const& i, 
           typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}

在实践中,我经常发现检查条件的能力是一种有用的能力。

票数 97
EN

Stack Overflow用户

发布于 2014-08-11 19:46:30

在C++11中,SFINAE测试变得更加漂亮。以下是一些常见用法的示例:

根据特征选择函数重载

代码语言:javascript
复制
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
}

使用所谓的类型接收器习惯用法,您可以对一个类型执行非常任意的测试,比如检查它是否有成员,以及该成员是否属于某个类型

代码语言:javascript
复制
//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;
}

这是一个活生生的例子:我最近还在我的博客上写了一整篇关于http://ideone.com/dHhyHE和标签分派的文章(无耻的插件,但相关) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html

注意:在C++14中,有一个std::void_t,它与我这里的TypeSink基本相同。

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/982808

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档