typedef struct 先声明后定义_C++模版和C#泛型求同存异录(二)typedef
(這篇文章有些復雜,請來回慢慢看)
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::DATATYPE,DATATYPE,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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python os.popen.read
- 下一篇: 计算机图形学多边形填充代码_零基础学计算