(这篇文章有些复杂,请来回慢慢看)

typedef

C++语言中的typedef简直是个神奇的关键字,它的最简单的作用就是把一种类型重新命名,定义个别名。很像宏定义,但又不是。在编程中使用typedef目的一般有两个,一个是给变量定义一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。但在C++模版编程之中,它还有个非常巧妙的用处:类型之间的传递和约束!

C++ typedef

来看个例子(真的用的好开心

template <class T>
class Matrix
{class Accessor{public:typedef T DATATYPE;}
}template <typename ACCESSOR>
class Scanner
{public:typedef typename ACCESSOR::DATATYPE DATATYPE;
}

一个简单的使用例子是这样的

typedef float DATATYPE;
Matrix<DATATYPE> data;    //数据源
Matrix<DATATYPE>::Accessor accessor(data); //访问器
Scanner<Matrix<DATATYPE>::Accessor> scanner(accessor) //检索器

请问Scanner::DATATYPE是什么数据类型?答案就是float!

也就是说,上面的案例之中,两个模版类的数据类型出现了传递,类型出现了相互依赖的关系。以上代码如果不做特殊设计,那么就只能写出这样的代码了

typedef float DATATYPE;
Matrix<DATATYPE> data;
Accessor<DATATYPE> accessor(data);
Scanner<DATATYPE> scanner(accessor)

逻辑是明白的,但是相互的依赖关系是要靠约定的,而不是代码本身能控制的。仅是typedef的巧妙使用,使得 T->Accessor::DATATYPE->Scanner::DATATYPE ,数据类型出现了传递行为。也就是说通过typedef,使的Scanner::DATATYPE,Matrix::Accessor::DATATYPEDATATYPE,float都是相同的类型,只是别名不同而已。这些不同的别名拥有不同的语义,且存在相互依赖关系。

类型传递?

那么更加进一步问题来了,C#能做到这个吗?

首先C#没有typedef关键字,也没有typename关键字,所以似乎我们只能写出最直白的代码(忽略using关键字定义别名的功能,这个功能太弱)

class Matrix<T>
{class Accessor{Accessor(Matrix<T> matrix){}}
}
class Scanner<T>
{Scanner(Matrix<T>.Accessor accessor){}
}Matrix<float> data;
Accessor<float> accessor(data);
Scanner<float> scanner(accessor)

这样的代码显然只能依赖于程序员不能犯错,而从代码本身角度来说,也是缺乏一些类型约束功能。

当然似乎这样的代码也不会出错,因为C#是强类型的,如果我们这样写

Matrix<int> data;
Accessor<float> accessor(data); //会报错,会提示无法从Matrix<int>转换成Matrix<float>
Scanner<float> scanner(accessor)

但是我期望的代码至少是

Matrix<float> test = new Matrix<float>();
Matrix<float>.Accessor accessor(data);
Scanner<Matrix<float>.Accessor> scanner(accessor)

让我们把C++的设计思路直接用在C#版本

尝试过程跳过,我给出一个最接近的版本。用到关键字dynamic,where,不懂的同学请自己查下帮助文档。

public class AccessorBase
{public dynamic value;
}public class Matrix<T>
{public class Accessor : AccessorBase{public new T value;}
}public class Scanner<Accessor> where Accessor : AccessorBase
{public Scanner(Accessor accessor){value = accessor.value;}private dynamic value; public dynamic GetValue() { return value; }
}

简化过的测试代码如下

Matrix<float>.Accessor accessor = new Matrix<float>.Accessor();
accessor.value = (float)99.0;
Scanner<Matrix<float>.Accessor> scanner = new Scanner<Matrix<float>.Accessor>(accessor);
dynamic tmp =  scanner.GetValue(); //tmp的值为null,但是我希望是99.0

问题出在 value = accessor.value; 这一句代码中 因为Accessor和AccessorBase的value只能保留一个。

不用where,那么accessor.value无法调用,除非使用反射。或者使用dynamic关键字

public Scanner(dynamic accessor)
{value = accessor.value;  //但accessor缺乏类型约束
}

反正我试了好久没有啥解决办法。回到C++的typedef功能 typedef

那么我们能在C#中模拟出来typedef吗,结论是可以!

直接上代码

public abstract class Typedef<T, TDerived> where TDerived : Typedef<T, TDerived>, new()
{private T _value;public static implicit operator T(Typedef<T, TDerived> t){return t == null ? default : t._value;}public static implicit operator Typedef<T, TDerived>(T t){return t == null ? default : new TDerived { _value = t };}
}

这是适合任何类型重定义的类。

class DATATYPE : Typedef<double, DATATYPE> { };
class SDATATYPE : Typedef<DATATYPE, SDATATYPE> { };

缺陷是DATATYPE和SDATATYPE是两个完全不同的类,因为C#是强类型。使用起来有些别扭

DATATYPE data = (DATATYPE)99.0;
SDATATYPE sdata = (SDATATYPE)(DATATYPE)99.0;

先忽略这一点,让我们看看前面的泛型代码该怎么改

默默测试两天之后,我发现,就算模拟了一个typedef的操作,依然还是做不到类型传递。

因为C#没法写出类似下面的代码

typedef typename ACCESSOR::DATATYPE DATATYPE;

typename ACCESSOR::DATATYPE是告诉C++编译器,ACCESSOR::DATATYPE是类型,虽然这个类型现在不知道是什么类型,但是就是个类型。然后C#可做不到这样的事情,所以只能是黑人问好脸了。

最后我又只能回到最初的代码,依赖于C#的强类型,保证在相互之间传递数据的时候,不会出错:

class Matrix<T>
{class Accessor{Accessor(Matrix<T> matrix){}}
}
class Scanner<T>
{Scanner(Matrix<T>.Accessor accessor){}
}Matrix<float> data;
Accessor<float> accessor(data);
Scanner<float> scanner(accessor)

后记

文章写完了,为了探索这个可能性,我大概花费了3-4个晚上的休息时间。虽然路是不通的,但也对C#泛型和C++的模板代码的设计思路更进一步了解了差异性。虽然这不算是什么成绩,但我还是把整个过程整理了一篇文章。

虽然Typedef 效果没有我设想的那么完美,但依然是一个很有效的typedef用法,特别可以支持任何Primitive类型,sealed class的继承操作,这一点还是值得一提的。

Github有个类似功能很小的C#库,叫LikeType,https://github.com/kleinwareio/LikeType 如果谁有兴趣可以仔细去看下,它也实现了类似的功能。

最后修改于2019-12-17日,首发于公众号:技术宅指北

typedef struct 先声明后定义_C++模版和C#泛型求同存异录(二)typedef相关推荐

  1. C++模版和C#泛型求同存异录(一)sizeof(T)

    sizeof(T) 从C++的模板代码往C#代码移植的时候发现了一个小问题. 在C++模板代码中 sizeof(T)是一种有效的写法,最终在会编译器展开成sizeof(int),sizeof(floa ...

  2. 全局声明宏定义_C++模拟面试:宏、lambda、智能指针闲谈

    有时候出于种种目的,我们会用宏来写一些函数.有人称之为宏函数.下面我们来模拟一场面试: 面试官 自来也 先来个简单的热热身,用宏实现求两个数最大值. 刷刷刷 #define MAX(x, y) ((x ...

  3. 【Struct(结构体)杂谈之二】名不正则言不顺---Struct(结构体)的声明、定义及初始化

    Struct(结构体)的声明.定义及初始化 上一篇里我们讲了为什么我们要引入Struct这个数据类型,我们了解到Struct是一种聚合数据类型,是为了用户描述和解释一些事物的方便而提出的,Struct ...

  4. typedef struct与struct的区别

    第一篇:typedef struct与struct的区别 1. 基本解释 typedef为C语言的关键字,作用是为一种数据类型定义一个新名字.这里的数据类型包括内部数据类型(int,char等)和自定 ...

  5. c语言中的typedef struct相当于java的一个类?,C ++中'struct'和'typedef struct'之间的区别?...

    在C ++中,之间有什么区别: struct Foo { ... }; 和 typedef struct { ... } Foo; #1楼 您不能对typedef结构使用forward声明. stru ...

  6. C/C++语法知识:typedef struct 用法详解

    第一篇:typedef struct与struct的区别 1. 基本解释 typedef为C语言的关键字,作用是为一种数据类型定义一个新名字.这里的数据类型包括内部数据类型(int,char等)和自定 ...

  7. 声明 struct x1 { ...}; 和 typedef struct { ...} x2; 有什么不同?

    声明 struct x1 { ...}; 和 typedef struct { ...} x2; 有什么不同? 第一种形式声明了一个 ``结构标签''; 第二种声明了一个 ``类型定义''. 主要的区 ...

  8. 【C语言】结构体定义 typedef struct 用法详解和用法小结

    结构体定义 typedef struct 用法详解和用法小结 文章目录 结构体定义 typedef struct 用法详解和用法小结 0. 前言 1. 首先:在C中定义一个结构体类型要用typedef ...

  9. 关于数据结构(c语言)中结构体声明的typedef struct LNode, *LinkList的思考

    在数据结构的链表,表示中 typedef struct Node * PtrToNode 和 typerdef struct List的表示说明 typedef struct Node * PtrTo ...

最新文章

  1. miniz库简介及使用
  2. CasperJs 入门介绍
  3. linux为启动菜单加密码
  4. Memcached简介
  5. Java PrintWriter close()方法与示例
  6. 架构设计 | 缓存管理模式,监控和内存回收策略
  7. 相机下载_索尼黑卡相机与手机互联APP相关
  8. Windows编程的Notification和Message
  9. vue ---- webpack中loader
  10. [Unity]Curvy插件随机生成装饰物
  11. SecureCRT配置详细图文教程
  12. val.substring is not a function
  13. C++算法之选择排序
  14. 建立时间setup time/保持时间 hold time
  15. python之奇数和或偶数和
  16. ERP系统的主要功能模块
  17. java实现倒计时闹钟_倒计时闹钟软件下载-倒计时闹钟app下载v1.2.4-西西软件下载...
  18. 一卡通系统中的前置机的设置
  19. 望帝春心托杜鹃 中望帝的由来~
  20. AutoCAD对象模型

热门文章

  1. linux dev_info,Linux命令集-xfs_info
  2. C++ priority_queue用法
  3. 银行家算法java代码
  4. 从一个被Tomcat拒绝的漏洞到特殊内存马
  5. 微软的漏洞攻击与防御
  6. 动态调试android, ddmsida
  7. 10、如何查看MySQL系统帮助?
  8. 1.18 Java直接插入排序法
  9. Webstorm/PhpStorm打开多个项目文件夹
  10. 【AC Saber】二分