Meta: SFINAE

在开始学习SFINAE之前我们首先要了解的是:

template parameters template arguments(针对C++17,不对C++11/14做过多考虑.).

 

(1) template parameters:

template < parameter-list > declaration 	

template parameters分为以下三种类型:

  • a non-type template parameter/argument;
  • a type template parameter/argument;
  • a template template parameter/argument;

  1), non-type template parameter

type name		
type name = default //例如: template<std::size_t number = 10>
type ... name
auto name 	//since c++17

上面的 type 必须为一下类型:

1,integral types

2,nullptr

3,lvalue reference type(to a object or a funcion)

4,pointer type(to a object or a function)

5,pointer to member(a member object pointer or a member function pointer)

6,enumeration type

7,C++17开始支持用auto:

template<auto n> struct B { /* ... */ };
B<5> b1;   // OK: non-type template parameter type is int
B<'a'> b2; // OK: non-type template parameter type is char
B<2.5> b3; // error: non-type template parameter type cannot be double

 

(2) type template parameter

typename name(optional)  // 1)
class name(optional) 	// 2)
typename|class name(optional) = default // 3) 
typename|class ... name(optional)  // 4)

  1), 一个 template paramter但是该parameter的名字是可选的(optional).

 2), 同上只是 声明 template parameter的关键字可以用class.

 3), 举个例子: template<typename T = int> 或者 template<typename = int>

 4),一个template parameter package但是该package的名字是可选的(optional).

 

(2.5) template template parameter

template < parameter-list > typename(C++17)|class name(optional) 	//1) 	
template < parameter-list > typename(C++17)|class name(optional) = default 	//2) 	
template < parameter-list > typename(C++17)|class ... name(optional) 	//3) 	(since C++11)

 1), 一个template template parameter带有一个可选的名字.

2),举个例子:   

template<typename T>
struct Node{};

template<template<typename> class nodeType = Node>
void function()
{
    nodeType<int> node;
}

3), 一个template template parameter package包的名字是可以选的.

 

(3) Template non-type arguments

1), non-type arguments可以在实例化object期间可以执行convert const expression.

demo 1:

template<const int* pci> struct X {};
int ai[10];
X<ai> xi;  // ok: array to pointer conversion and cv-qualification conversion
 
struct Y {};
template<const Y& b> struct Z {};
Y y;
Z<y> z;  // ok: no conversion
 
template<int (&pa)[5]> struct W {};
int b[5];
W<b> w; // ok: no conversion
 
void f(char);
void f(int);
template<void (*pf)(int)> struct A {};
A<&f> a; // ok: overload resolution selects f(int)

2)当non-type parameter为 reference/pointer的时候:

    (1), 不能ref/point 一个class object的 non-static member data/基类,以及一个数组的成员

    (2), 不能ref/point 一个临时的object

    (3), 不能ref/point 一个 string literal(比如 "shihuamarryme")

    (4), 不能ref/point typeid(type)的结果.

    (5), 不能ref/point __func__;

demo 2:

template<typename T> class A { int x; }; // primary template
template<class T> class A<T*> { long x; }; // partial specialization
 
// class template with a template template parameter V
template<template<typename> class V> class C
{
    V<int> y; // uses the primary template
    V<int*> z; // uses the partial specialization
};
 
C<A> c; // c.y.x has type int, c.z.x has type long

demo 3:

template<typename T> struct eval; // primary template 
 
template<template<typename, typename...> class TT, typename T1, typename... Rest>
struct eval<TT<T1, Rest...>> {}; // partial specialization of eval
 
template<typename T1> struct A;
template<typename T1, typename T2> struct B;
template<int N> struct C;
template<typename T1, int N> struct D;
template<typename T1, typename T2, int N = 17> struct E;
 
eval<A<int>> eA; // ok: matches partial specialization of eval
eval<B<int, float>> eB; // ok: matches partial specialization of eval
eval<C<17>> eC; // error: C does not match TT in partial specialization because
                // TT's first parameter is a type template parameter,
                // while 17 does not name a type
eval<D<int, 17>> eD; // error: D does not match TT in partial specialization
                     // because TT's second parameter is a type parameter pack,
                     // while 17 does not name a type
eval<E<int, float>> eE; // error: E does not match TT in partial specialization
                        // because E's third (default) parameter is a non-type

demo 4:

template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template <class ...Types> class C { /* ... */ };
 
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK in C++14 after CWG 150
         // Error earlier: not an exact match
X<C> xc; // OK in C++14 after CWG 150
         // Error earlier: not an exact match
 
template<template<class ...> class Q> class Y { /* ... */ };
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
 
template<auto n> class D { /* ... */ }; // note: C++17
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // OK
 
template <int> struct SI { /* ... */ };
template <template <auto> class> void FA();  // note: C++17
FA<SI>();  // Error

 

 

SFINAE

1), 什么是SFINAE?

Substitution Failure Is Not An Error(替换失败不是一个错误).

2),什么是Expression(In C++)?

看这里: http://en.cppreference.com/w/cpp/language/expressions

3),什么是Expression SFINAE?

Expression SFINAE(只是属于SFINAE的一种,如果想查看所有的SFINAE的特性请参阅这里):

根据上面的总结为2点:

1, 无效的表达式用在模板(function/class/struct的)参数类型

2, 无效的表达式用在函数返回类型

demo1 for function-type

struct X {};
struct Y { Y(X){} }; // X is convertible to Y
 
template <class T>
auto f(T t1, T t2) -> decltype(t1 + t2); // overload #1
 
X f(Y, Y);  // overload #2
 
X x1, x2;
X x3 = f(x1, x2);  // deduction fails on #1 (expression x1+x2 is ill-formed)
                   // only #2 is in the overload set, and is called

demo2 for template parameter type

#include <iostream>
#include <type_traits>
#include <utility>


//注意这里在vs是有bug的要去gcc/clang且支持c++14

int* function(int* ptr)noexcept
{
	return ptr;
}


template<typename ...>
using void_t = void;

template<typename... Ts> struct make_void { typedef void type; };
template<typename... Ts> using void_t = typename make_void<Ts...>::type;

template<typename T>
using type = decltype(function(std::declval<T*>()));

template<typename Ty, typename = void_t<>>
struct Test : public std::false_type {};

template<typename Ty>//speciafization
struct Test<Ty, void_t<type<Ty>>> : public std::true_type { };


int main()
{
	std::cout << std::boolalpha;
	std::cout << Test<int>::value << std::endl;

	return 0;
}

 

demo3 for template constructor

这个例子必须要借助SFINAE才能正确运行.

因此我们需要特别注意的是:

 1, 构造函数尽量不要定义为泛化构造函数

#include <iostream>
#include <typeinfo>


class Person{
 private:
  std::string str;
  
 //----------------------重点.
  class Get{
   private:
    int idx;
    
   public:
    using value_type = std::string;
    
    Get()=default;
    
    ~Get()=default;
    
    constexpr Get(const int& index):idx(index){};
    
    value_type operator()(const int& index) noexcept
    {
     return std::string("shihuawoaini");
    }
    
    operator value_type() noexcept
    {
     return (*this)(idx);
    }
  };
  //--------------------------
  
  public:
   Person()=default;
   
   template<typename T>
   explicit Person(T&& t);//泛化构造函数!
   
   explicit Person(int idx);
   
   Person(const Person& p);
   
   Person(Person&& p);
   
   virtual ~Person()=default;
};


template<typename T>
Person::Person(T&& t)
       :str(std::forward<T>(t))
{
 std::cout<<"T-type"<<std::endl;
 std::cout<<typeid(t).name()<<std::endl;
 std::cout<<std::boolalpha<<std::is_same<const char (&)[13], T>::value<<std::endl;
}

template<typename T>
Person::Person(T&& t)
       :str(std::forward<T>(t))
{
 std::cout<<"T-type"<<std::endl;
 std::cout<<typeid(t).name()<<std::endl;
 std::cout<<std::boolalpha<<std::is_same<const char (&)[13], T>::value<<std::endl;
}


Person::Person(int idx)
       :str(Get(idx))
{
 std::cout<<"-----------"<<std::endl;
}

Person::Person(const Person& p)
       :str(p.str)
{
 //
}


Person::Person(Person&& p)
       :str(std::move(p.str))
{
 
}

class SpecialPerson : public Person{
 private:
  std::string str_;
  
  public:
   SpecialPerson(const std::string& base, const std::string& inherit):Person(base),str_(inherit){}
   virtual ~SpecialPerson()=default;
   
   SpecialPerson(const SpecialPerson& sp)noexcept;
   SpecialPerson(SpecialPerson&& sp)noexcept;
   
   
};

SpecialPerson::SpecialPerson(const SpecialPerson& sp)noexcept
              :Person(sp),
               str_(sp.str_)
{
 //
}

SpecialPerson::SpecialPerson(SpecialPerson&& sp)noexcept
              :Person(sp),
               str_(std::move(sp.str_))
{
 //
}
int main()
{
 Person p1(std::string("shihuawoaini"));
 Person p2("shihuawoaini");
 
 //case 1:
 Person p3(2.5); //这里我们如果传入进去参数2.5,会调用模板构造函数,进而造成构造失败.
 //为什么呢?
 //因为2.5是double类型, 那么这么一来double类型转到int类型需要一次类型转换,而模板构造函数不需要转换
 //也就提供了更加精确的匹配. 
 
 //case 2:
 Person p3(p2); //这里调用了构造函数而不是拷贝构造函数.
 
 //case 3:
 SpecialPerson sp1(std::string("shihuawoani"), std::string("marry me"));
 SpecialPerson sp2(sp1);
 //这里会调用基类中的构造函数而不是基类中的拷贝构造函数!!!!!!!!
 //因为调用基类中的拷贝构造函数需要进行派生类向基类的转换.
 //而调用基类向派生类的转换是不需要的,因此匹配度更佳. 
 
 const Person p4("shihuawoaini");
 Person p5(p4);  //OK调用了拷贝构造函数. 
 Person p6(5); //OK调用了接受int的构造函数. 
 
 return 0;
}

 

demo 4:

#include <iostream>

class Person{
 private:
  std::string str;
  
  class Get{
   private:
    int idx;
    
    public:
     using value_type = std::string;
     
     constexpr Get(const int& index):idx(index){}
     
     value_type operator()(const int& index)noexcept
     {
      return std::string("shihuawoaini");
     }
     
     operator value_type()noexcept
     {
      return (*this)(idx);
     }
     
  };
  
  public:
   //注意这里的模板声明. 
   template<typename T, typename = typename std::enable_if< 
   !std::is_base_of<Person, typename std::decay<T>::type>::value
   &&
   !std::is_integral< typename std::remove_reference<T>::type >::value
   &&
   !std::is_same<double, typename std::decay<T>::type>::value
   &&
   !std::is_same<const char16_t*, typename std::decay<T>::type>::value //解决utf-8的问题. 
   >::type>
   Person(T&& t);
   
   Person(const int& index);
   
   Person(const Person& p);
   
   Person(Person&& p);
   
   virtual ~Person()=default;
};


template<typename T, typename>
Person::Person(T&& t)
       :str(std::forward<T>(t))
{
 //
 std::cout<<"template"<<std::endl;
}


Person::Person(const int& index)
       :str(Get(index))
{
 //
 std::cout<<"number"<<std::endl;
}


Person::Person(const Person& p)
       :str(p.str)
{
 //
}

Person::Person(Person&& p)
       :str(std::move(p.str))
{
 //
}


class SpecialPerson : public Person{
 private:
  std::string str_;
  
  public:
   SpecialPerson(const std::string& base, const std::string& inherit):Person(base),str_(inherit){}
   
   virtual ~SpecialPerson()=default;
   
   SpecialPerson(const SpecialPerson& sp);
   
   SpecialPerson(SpecialPerson&& sp);
};


SpecialPerson::SpecialPerson(const SpecialPerson& sp)
              :Person(sp),
               str_(sp.str_)
{
 //
}

SpecialPerson::SpecialPerson(SpecialPerson&& sp)
              :Person(sp),
               str_(std::move(sp.str_))
{
 //
}

int main()
{
 Person p1("shihuawoaini");
 Person p2(std::string("shihuawoaini"));
 //Person p(u"shihuawoaini"); //这里的 (u"shihuawoaini")是const char16_t[13]类型. std::string并没有构造函数结构这样的参数. 
 
 //qeustion 1: 
 Person p3(p2);
 
 //question 2: 
 Person p4(2);
 Person p5(3.14);
 
 //question 3:
 SpecialPerson sp1(std::string("shihua"), std::string("MarryMe"));
 SpecialPerson sp2(sp1);
 
 return 0;
}

 

demo 5 for tags dispatch(标签分配)

 #include <iostream>
#include <type_traits>


template<typename T>
void logAndAddImpl(T&& name, std::true_type)
{
 std::cout<<"true_type"<<std::endl;
}

template<typename T>
void logAndAddImpl(T&& name, std::false_type)
{
 std::cout<<"false_type"<<std::endl;
}

template<typename T>
void logAndAdd(T&& name)
{
 logAndAddImpl( std::forward<T>(name), std::is_integral< typename std::remove_reference<T>::type>()); //注意这里.
 //上面用的是std::is_integral<typename std::remove_reference<T>::type>());
 //而不是std::is_integral<typename std::remove_reference<T>::type>::value)
}


int main()
{
 std::string str("shihuawoaini");
 logAndAdd(str);
 
 logAndAdd(10);
 return 0;
}

 

 

转载于:https://my.oschina.net/SHIHUAMarryMe/blog/667774

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值