对SFINAE(替换失败并非错误)的理解

今晚从《C++必知必会》上看到SFINAE这个C++的特性,也就是substitution failure is not an error,可惜怎么看都不能够理解。后来google了一下,参考了两篇文章,算是有点明白其中的微妙了吧。

   我们都知道对于非模板函数的重载来说,无论是否被调用,或是无论调用点需要的是什么类型的重载,编译器会将所有参与了重载的函数一个不落的全部编译。而且这些函数的所有信息已经具备,当进行调用的时候,编译器就能根据参数的个数跟类型来调用相关度最高的函数。但对于模板函数来说就不一样了,因为事先编译器根本无法获得所有信息,编译器也不可能为所有重载的模板函数生成真正的执行代码,而是会选择最相关的模板函数进行实例化。看下面的一个例子(引自http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/748139d003b74088a1ec9c07.html):

#include <iostream>
using namespace std;
void print( int iNum )
{
         cout<<"int print( int )"<< endl;
}
template < typename _Ty >
void print( _Ty tt )
{
           typename _Ty::value_type vt_someval;
           cout<<"template < typename _Ty >"<< endl;
}
int main()
{
         short siNum = 10;
         print( siNum );
         return 0;
}

 

   这段代码是不能经过编译的,原因是:当进行编译的时候,编译器放弃了对short进行向上转换为int,而是选择了模板函数进行实例化。刚开始用short来实例化模板函数是最合适的,可是在实例化的时候就出现问题了,因为int::value_type是错误的。

 

    再看下面的一个例子:

#include <iostream>
using namespace std;
void print( int iNum )
{
         cout<<"int print( int )"<< endl;
}
template < typename _Ty >
void print( _Ty tt, typename _Ty::value_type* pvt_dummy = NULL )
{
           typename _Ty::value_type vt_someval;
           cout<<"template < typename _Ty >"<< endl;
}
int main()
{
         short siNum = 10;
         print( siNum );
         return 0;
}

    这时候发现能够编译成功,输出的信息为:int print(int)。

 

    这次为什么能够成功调用void print(int iNum)呢?对于第一次的代码,由于对于模板函数来说,返回值跟参数都能匹配成功,就表示编译器会认为特化成功而选择模板函数进行特化进而放弃其他选择,这时候自然就会产生错误。但是多了typename _Ty::value_type*后,编译器在展开的时候就会发现错误。这时由于SFINAE的存在,编译器就会放弃特化转而去选择void print(int INum),而不是简单报错。这就是SFINAE,C++一个非常重要的属性。

看最后一个例子:

template <typename T>
bool is_class(int T::*) {
    return true;
}

template <typename T>
bool is_class(...) {
    return false;
}

struct Test {
};

int main(void) {
    std::cout<<is_class<Test>(0)<<endl;
    std::cout<<is_class<int>(0)<<endl;
}

输出的结果为1跟0。对于第二个调用来说,刚开始选择的时候由于int T::*匹配优先级比...高,所以编译器会选择第一个尝试进行展开,但随后编译器会发现错误,这时候编译器只是简单地放弃特化,进而选择第二个。试想一下,假如没有SFINAE的存在,编译器这时候就会报错,导致无法编译通过,进而其余的模板函数得不到被选择的机会,导致很多对重载函数模板的使用都是非法的。

    总体说来,SFINAE就是在假如有一个物化可能产生编译错误的情况下,如果还有其他选择,就忽略这个错误,进而选择其他的重载函数,而不会导致报错。

你可能感兴趣的:(in)