首页 > 其他分享 >元模板 笔记

元模板 笔记

时间:2022-10-04 11:44:06浏览次数:89  
标签:struct DoWork 笔记 template type 模板 特化

对类型编写,由于c++不存在

if(type == xxx) {}

这种语法。

类型计算可以使用:

1,重载。

2,虚函数。继承。

3,c语言中利用 Union

查看代码
struct Variant
{
    union
    {
        int x;
        float y;
    } data;
    uint32 typeId;
};

Variant addFloatOrMulInt(Variant const* a, Variant const* b)
{
    Variant ret;
    assert(a->typeId == b->typeId);
    if (a->typeId == TYPE_INT)
    {
        ret.x = a->x * b->x;
    }
    else
    {
        ret.y = a->y + b->y;
    }
    return ret;
}

4,void*

查看代码
 #define BIN_OP(type, a, op, b, result) (*(type *)(result)) = (*(type const *)(a)) op (*(type const*)(b))
void doDiv(void* out, void const* data0, void const* data1, DATA_TYPE type)
{
    if(type == TYPE_INT)
    {
        BIN_OP(int, data0, *, data1, out);
    }
    else
    {
        BIN_OP(float, data0, +, data1, out);
    }
}

5,C++中比如在 Boost.Any 的实现中,运用了 typeid 来查询类型信息。

6,和 typeid 同属于RTTI机制的 dynamic_cast,也经常会用来做类型判别的工作。

查看代码
 IAnimal* animal = GetAnimalFromSystem();

IDog* maybeDog = dynamic_cast<IDog*>(animal);
if(maybeDog)
{
    maybeDog->Wangwang();
}
ICat* maybeCat = dynamic_cast<ICat*>(animal);
if(maybeCat)
{
    maybeCat->Moemoe();
}

用模板后

在模板代码中,这个“合适的机制”就是指“特化”和“部分特化(Partial Specialization)”,后者也叫“偏特化”。

特化:

// 我们这个模板的基本形式是什么?
template <typename T> class AddFloatOrMulInt;

// 但是这个类,是给T是Int的时候用的,于是我们写作
class AddFloatOrMulInt<int>
// 当然,这里编译是通不过的。

// 但是它又不是个普通类,而是类模板的一个特化(特例)。
// 所以前面要加模板关键字template,
// 以及模板参数列表
template </* 这里要填什么? */> class AddFloatOrMulInt<int>;

// 最后,模板参数列表里面填什么?因为原型的T已经被int取代了。所以这里就不能也不需要放任何额外的参数了。
// 所以这里放空。
template <> class AddFloatOrMulInt<int>
{
    // ... 针对Int的实现 ... 
}

// Bingo!

 

类型参数相当于函数的参数,里面的定义的成员值,相当于返回值。定义多个成员,意味着可以返回多个值了!!

//对于一般没匹配特例的,参数是T,类型函数返回值是 -2.
template <typename T> class TypeToID
{
public:
    static int const NotID = -2;
};
//对于float参数,返回值是1。注意取值时,可以自己写了 ID. 而上面用的是 NotID
template <> class TypeToID<float>
{
public:
    static int const ID = 1;
};

类模板和类模板的特化的作用,仅仅是指导编译器选择哪个编译,但是特化之间、特化和它原型的类模板之间,是分别独立实现的。所以如果多个特化、或者特化和对应的类模板有着类似的内容,很不好意思,你得写上若干遍了。

重载与模板

重载:

void doWork(int);
void doWork(float);
void doWork(int, int);

void f() {
    doWork(0);
    doWork(0.5f);
    doWork(0, 0);
}

对应模板

template <typename T> struct DoWork;     // (0) 这是原型 相当于函数

//下面的<int>等想当于实参
template <> struct DoWork<int> {};       // (1) 这是 int 类型的"重载"
template <> struct DoWork<float> {};     // (2) 这是 float 类型的"重载"
template <> struct DoWork<int, int> {};  // (3) 这是 int, int 类型的“重载”

void f(){
    DoWork<int>      i;
    DoWork<float>    f;
    DoWork<int, int> ii;
}

 所以DoWork<int, int>会导致编译错误,实参和原型不对应啊。因为原型里面只有一个参数 T。<int,int>是两个参数。

 

练习:

#include <memory>

using std::unique_ptr;
using std::shared_ptr;

template <typename T, typename U> struct X            ;    // 0 原型有两个类型参数
                                                           // 所以下面的这些偏特化的实参列表
                                                           // 也需要两个类型参数对应

// 下面的这些偏特化的“小尾巴”也需要两个类型参数对应
template <typename T>             struct X<T,  T  > {};    // 1
template <typename T>             struct X<T*, T  > {};    // 2
template <typename T>             struct X<T,  T* > {};    // 3
template <typename U>             struct X<U,  int> {};    // 4
template <typename U>             struct X<U*, int> {};    // 5
template <typename U, typename T> struct X<U*, T* > {};    // 6
template <typename U, typename T> struct X<U,  T* > {};    // 7

template <typename T>             struct X<unique_ptr<T>, shared_ptr<T>>; // 8


// 以下特化,分别对应哪个偏特化的实例?
// 此时偏特化中的T或U分别是什么类型?

X<float*,  int>      v0;           //5            
X<double*, int>      v1;           //5         
X<double,  double>   v2;             //1             
X<float*,  double*>  v3;               //6            
// X<float*,  float*>   v4;                          
X<double,  float*>   v5;   //7                       
X<int,     double*>  v6;       //7                    
//X<int*,    int>      v7;                       
X<double*, double>   v8; //2

在上面这段例子中,有几个值得注意之处。首先,偏特化时的模板形参,和原型的模板形参没有任何关系。和原型不同,它的顺序完全不影响模式匹配的顺序,它只是偏特化模式,如<U, int>U的声明,真正的模式,是由<U, int>体现出来的。

这也是为什么在特化的时候,当所有类型都已经确定,我们就可以抛弃全部的模板参数,写出template <> struct X<int, float>这样的形式:因为所有列表中所有参数都确定了,就不需要额外的形式参数了。

其次,作为一个模式匹配,偏特化的实参列表中展现出来的“样子”,就是它能被匹配的原因。比如,struct X<T, T>中,要求模板的两个参数必须是相同的类型。而struct X<T, T*>,则代表第二个模板类型参数必须是第一个模板类型参数的指针,比如X<float***, float****>就能匹配上。当然,除了简单的指针、constvolatile修饰符,其他的类模板也可以作为偏特化时的“模式”出现,例如示例8,它要求传入同一个类型的unique_ptrshared_ptr。C++标准中指出下列模式都是可以被匹配的:

N3337, 14.8.2.5/8

T是模板类型实参或者类型列表(如 int, float, double 这样的,TT是template-template实参(参见6.2节),i是模板的非类型参数(整数、指针等),则以下形式的形参都会参与匹配:

T,cv-list T,T*template-name <T>T&T&&

T [ integer-constant ]

type (T)T()T(T)

T type ::*type T::*T T::*

T (type ::*)()type (T::*)()type (type ::*)(T)type (T::*)(T)T (type ::*)(T)T (T::*)()T (T::*)(T)

type [i]template-name <i>TT<T>TT<i>TT<>

对于某些实例化,偏特化的选择并不是唯一的。比如v4的参数是<float*, float*>,能够匹配的就有三条规则,1,6和7。很显然,6还是比7好一些,因为能多匹配一个指针。但是1和6,就很难说清楚谁更好了。一个说明了两者类型相同;另外一个则说明了两者都是指针。所以在这里,编译器也没办法决定使用那个,只好爆出了编译器错误。

其他的示例可以先自己推测一下, 再去编译器上尝试一番:goo.gl/9UVzje

 

非要用重载:

DoWork<int,   void> i;
DoWork<float, void> f;
DoWork<int,   int > ii;

这时,我们就能写出统一的模板原型:

template <typename T0, typename T1> struct DoWork;

继而偏特化/特化问题也解决了:

template <> struct DoWork<int,   void> {};  // (1) 这是 int 类型的特化
template <> struct DoWork<float, void> {};  // (2) 这是 float 类型的特化
template <> struct DoWork<int,    int> {};  // (3) 这是 int, int 类型的特化

 

用默认模板参数,解决不停的写void的问题

template <typename T0, typename T1 = void> struct DoWork;

template <typename T> struct DoWork<T> {};
template <>           struct DoWork<int> {};
template <>           struct DoWork<float> {};
template <>           struct DoWork<int, int> {};

DoWork<int> i;
DoWork<float> f;
DoWork<double> d;
DoWork<int, int> ii;

终极用变长模板参数

template <typename... Ts, typename U> class X {};              // (1) error!
template <typename... Ts>             class Y {};              // (2)
template <typename... Ts, typename U> class Y<U, Ts...> {};    // (3)
template <typename... Ts, typename U> class Y<Ts..., U> {};    // (4) error!

为什么第(1)条语句会出错呢?(1)是模板原型,模板实例化时,要以它为基础和实例化时的类型实参相匹配。因为C++的模板是自左向右匹配的,所以不定长参数只能结尾。其他形式,无论写作Ts, U,或者是Ts, V, Us,,或者是V, Ts, Us都是不可取的。(4) 也存在同样的问题。

但是,为什么(3)中, 模板参数和(1)相同,都是typename... Ts, typename U,但是编译器却并没有报错呢?

答案在这一节的早些时候。(3)和(1)不同,它并不是模板的原型,它只是Y的一个偏特化。回顾我们在之前所提到的,偏特化时,模板参数列表并不代表匹配顺序,它们只是为偏特化的模式提供的声明,也就是说,它们的匹配顺序,只是按照<U, Ts...>来,而之前的参数只是告诉你Ts是一个类型列表,而U是一个类型,排名不分先后。

 


template <>
class lazy_string_concat_helper<>

template <typename... Strings>
class lazy_string_concat_helper;


template <typename LastString, typename... Strings>
class lazy_string_concat_helper<LastString,
                                Strings...>

lazy_string_concat_helper<std::string> 将被匹配成:lazy_string_concat_helper<std::string, lazy_string_concat_helper<>>

我们看到lazy_string_concat_helper的匹配模板有3个。这3个里面符合<std::string>的只有第二个<typename... Strings>, 这样模板会自动生成特例化:

template <>
class lazy_string_concat_helper<typename std::string>;

这个是不是没有实现,又和其他2个最终实现匹配到了第3个:

template <typename LastString, typename... Strings>
class lazy_string_concat_helper<LastString,
                                Strings...> 

最终的特例化是:

template <>  class lazy_string_concat_helper<typename std::string,  <> >;

后面的<>又匹配到第一个实现。即特例化成:

template<>
class lazy_string_concat_helper<std::basic_string<char> >{

public: 
  inline lazy_string_concat_helper(std::basic_string<char> data, lazy_string_concat_helper<> tail)
  : data{std::basic_string<char>(data)}
  , tail{lazy_string_concat_helper<>(tail)}
  {
  }

}

 

标签:struct,DoWork,笔记,template,type,模板,特化
From: https://www.cnblogs.com/bigben0123/p/16708697.html

相关文章

  • <<程序员修炼之道:从小工到专家>>阅读笔记01
       <<程序员修炼之道:从小工到专家>>阅读笔记01第一章注重实效的哲学1.注重实效的程序员的特征:是他们处理问题、寻求解决方案时的态度、风格、哲学。设法把问题放......
  • C++ 自学笔记 Declaring references 引用
    引用:类型&Y=X;Y是X的另一个名字   pointersvsreferences(指针vs引用)(ps:引用就是用的const指针)  java:所有对象放在堆里只能用指针(不能计算用的const)访......
  • 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin
    原文链接我是一名五六年经验的前端程序员,现在准备学习一下Golang的后端框架gin。以下是我的学习实战经验,记录下来,供大家参考。https://github.com/gin-gonic/gin1.......
  • 新概念第一册21~30单元学习笔记
    Chaptertwenty-one:Whichbook?#名词单数+特殊疑问DialogueGivemeabookplease,Jane#祈使句,动词原形开头Whichbook?#口语省略形式,原句是whichbookdoyouwant?T......
  • C++ 自学笔记 const
    const变量  const是变量不是常量 指针const写在*号后面       const函数可以构成重载 ......
  • Modular int模板
    防止一切因为忘记模数而导致爆0的事情发生!template<intmod>structmint{unsignedint_v;mint():_v(0){}template<classT>mint(Tv){......
  • C++自学笔记 内联函数 Inline Function
      调用一个函数需要额外做一些什么:推入参数进堆栈推入返回地址进堆栈准备返回值在寄存器推出所有推入解决方法:内联函数InlineFunction  内联函数可以......
  • 笔记一:机器学习工作流程
    目录1理解问题和背景1.1目的1.2工作环境1.3获取数据2探索性数据分析(EDA)3数据预处理3.1数据清理3.2特征选择3.3特征工程3.4特征缩放4模型探索根据Geron(2019)......
  • 学习笔记:python字符串的处理方法
    python学习字符串处理方法1.str.lower()和str.upper()实现全大写和全小写。2.str.split()能够使字符串以一种格式分割开,并返回一个分割完成的列表。3.str.count(x)......
  • 学习笔记;python循环
    python学习1.for循环foriinrange(1,10):print(i)#输出为123456789for循环与列表的结和squares=[]fornumberinrange(1,10):square=......