1. 程式人生 > >【c++】由構建複數類簡析c++的user defined literal特性

【c++】由構建複數類簡析c++的user defined literal特性

User defined literal是c++提供的特性,可以讓程式設計者僅用常規的幾個資料型別來初始化自定義的類物件,而不需要顯式呼叫類構造/拷貝方法,使程式碼可讀性更高。但實際上這本質上還是是運算子的過載,還是要呼叫一部分方法。下文記錄了嘗試該特性的實驗,這個小實驗以構建一個複數類為契機,簡單瞭解一下udl特性。

首先構建一個簡單的複數類模板,只實現讀取/賦值實部虛部的介面:

template <typename T> class ComplexBase {
public:
	ComplexBase() = default;

	T re() const {return x;}
	T& re() {return x;}
	T im() const {return y;}
	T& im() {return y;}

protected:
	T x;
	T y;
};

對該類派生出一個實虛部都是整數型別的虛數類以及相應的過載方法:

class ComplexInt : public ComplexBase<int> {
public:
	ComplexInt() = default; // explicit declaration, avoid being ambiguous
	ComplexInt(int i, int j) { x = i; y = j; }

	/* Leave copy and = to default */

	ComplexInt(int i) { x = i; y = 0;} // converting constructor

	inline friend std::ostream& operator<< (std::ostream& os, ComplexInt c) {
		os << c.re() << " + " << c.im() << "i";
		return os;
	}
};

inline ComplexInt operator+ (const ComplexInt c1, const ComplexInt c2) {
	ComplexInt c;
	c.re() = c1.re() + c2.re();
	c.im() = c1.im() + c2.im();
	return c;
}

inline ComplexInt operator- (const ComplexInt c1, const ComplexInt c2) {
	ComplexInt c;
	c.re() = c1.re() - c2.re();
	c.im() = c1.im() - c2.im();
	return c;
}

inline ComplexInt operator* (const ComplexInt c1, const ComplexInt c2) {
	ComplexInt c;
	c.re() = c1.re() * c2.re() - c1.im() * c2.im();
	c.im() = c1.re() * c2.im() + c1.im() * c2.re();
	return c;
}

到此為止一個可以使用的複數類就實現了,在主函式裡可以這樣呼叫:

int main() {

	ComplexInt c1(1, 1), c2(1, -1);
	cout << c1 + c2 << endl;
	cout << c1 * c2 << endl;
}

但是諸如c(x, y)這樣的初始化方法還是太不直觀了,使用者希望使用c = x + y_i這樣的形式來構建簡單的複數物件。

literal運算子或者說operator "",和其他型別的運算子一樣,可以過載亦可模板化,甚至可以作為類的友元,其形式為

template <T1> T2 operator "" _x();

注意!literal過載不能寫在類宣告內,必須置於外部。如果放在類宣告內,報錯:

error: literal operator 'operator""_i' must be in a namespace or global scope
        ComplexInt operator"" _i (int j) {return ComplexInt(0, j);}

其中T1只能是常用型別中的unsigned long long, long double, char, const char*這幾個型別,當然還有其他型別但不常用,就不列出了。在這個複數類例子中,過載為:

ComplexInt operator"" _i (unsigned long long j) {return ComplexInt(0, j);}

該過載將一個代下表的整數轉換成一個ComplexInt類的純虛數。

通過原本就實現的型別轉換,可以實現將一個整數轉換成ComplexInt類的純實數。於是可以在主函式裡呼叫:

int main() {

	ComplexInt c1 = 1 + 1_i, c2 = 1 - 1_i;
	cout << c1 + c2 << endl;
	cout << c1 - c2 + 1 << endl;
	cout << c1 * c2 << endl;
	cout << c1 * c2 - 2_i << endl;
}

輸出:

2 + 0i
1 + 2i
2 + 0i
2 + -2i
如有錯誤請指正,我什麼都會做的

相關推薦

c++構建複數c++的user defined literal特性

User defined literal是c++提供的特性,可以讓程式設計者僅用常規的幾個資料型別來初始化自定義的類物件,而不需要顯式呼叫類構造/拷貝方法,使程式碼可讀性更高。但實際上這本質上還是是運算子的過載,還是要呼叫一部分方法。下文記錄了嘗試該特性的實驗,這個小實驗以構

C++實現一個複數(complex)(帶有預設引數的建構函式 )

/*實現一個複數類(complex) class complex { private: double _real; double _image; }; */ #include <iostream

Android三種工廠模式

簡單介紹三種工廠模式:簡單工廠模式,工廠方法模式,抽象工廠模式 1.簡單工廠模式 這是一個小工廠,什麼都由自己生產,別人要什麼就呼叫自己的相應工具去生產 具備三個特徵:具體工廠A,具體生產工具B,抽象生產物件C 就是 A呼叫工具B去生產C

編寫高質量代碼改善C#程序的157個建議——建議8: 避免給枚舉型的元素提供顯式的值

bsp clas val () spa true tel str none 建議8: 避免給枚舉類型的元素提供顯式的值 一般情況下,沒有必要給枚舉類型的元素提供顯式的值。創建枚舉的理由之一,就是為了代替使用實際的數值。不正確地為枚舉類型的元素設定顯式的值,會帶來意想不到

編寫高質量代碼改善C#程序的157個建議——建議35:使用default為泛型型變量指定初始值

如果 items item 類型變量 color 高質量 使用 per match 建議35:使用default為泛型類型變量指定初始值 有些算法,比如泛型集合List<T>的Find算法,所查找的對象可能會是值類型,也有可能是引用類型。在這種算法內部,我

編寫高質量代碼改善C#程序的157個建議——建議45:為泛型型參數指定逆變

str 質量 red 方法的參數 turn test col nbsp 改善 建議45:為泛型類型參數指定逆變 逆變是指方法的參數可以是委托或者泛型接口的參數類型的基類。FCL4.0中支持逆變的常用委托有: Func<int T,out TResult> P

編寫高質量代碼改善C#程序的157個建議——建議90:不要為抽象提供公開的構造方法

改善 公開 構造方法 編譯 只需要 高質量代碼 pub 默認 {} 建議90:不要為抽象類提供公開的構造方法 首先,抽象類可以有構造方法。即使沒有為抽象類指定構造方法,編譯器也會為我們生成一個默認的protected的構造方法。下面是一個標準的最簡單的抽象類:

編寫高質量代碼改善C#程序的157個建議——建議97:優先考慮將基型或接口作為參數傳遞

span sta his 正是 子集 bsp iter 泛型接口 成員 建議97:優先考慮將基類型或接口作為參數傳遞 除了公開及類型或接口外,方法的參數也應該考慮基類型或接口。 以Enumerable類型為例,它的成員方法中只要涉及需要操作集合對象的地方,都要使用IEn

編寫高質量代碼改善C#程序的157個建議——建議99:重寫時不應使用子參數

bsp man stat pub ati lin set 薪水 col 建議99:重寫時不應使用子類參數 重寫時,如果使用了子類參數,可能會偏離設計者的預期目標。比如,存在一個如下繼承體系: class Employee { } cl

編寫高質量代碼改善C#程序的157個建議——建議107:區分靜態和單例

滿足 高質量代碼 同時 對象 method 導致 建議 單例 繼承 建議107:區分靜態類和單例 有一種觀點認為:靜態類可以作為單件模式的一種實現方式。事實上,這是不妥當的。按照傳統的觀點來看,單例是一個實例對象。而靜態類並不滿足這一點。靜態類也直接違反面向對象三大特性

編寫高質量代碼改善C#程序的157個建議——建議101:使用擴展方法,向現有型“添加”方法

() sealed 返回 res turn 擴展方法 需求 write 字符串 建議101:使用擴展方法,向現有類型“添加”方法 考慮如何讓一個sealed類型具備新的行為。以往我們會創建一個包裝器類,然後為其添加方法,而這看上去一點兒也不優雅。我們也許會考慮修改設計,

編寫高質量代碼改善C#程序的157個建議——建議102:區分接口和抽象的應用場合

支持 完成 不同 作用 設計 來看 適合 c# 職責 建議102:區分接口和抽象類的應用場合 接口和抽象類有一些顯而易見的區別: 接口支持多繼承,抽象類則不能。 接口可以包含方法、屬性、索引器、事件的簽名,但不能有實現,抽象類則可以。 接口在增加新方法後,所有的繼承

C++使用sizeof計算物件所佔空間大小-sizeof總結

 決定C ++中物件的大小的因素: 1.所有非靜態資料成員的大小 2.資料成員的順序 3.位元組對齊或位元組填充 4.其直接基類的大小虛擬函式的存在 5.  正在使用的編譯器 6.繼承模式(虛擬繼承) 一、使用sizeof計算類物件所佔空間大小

C#面對物件和、構造方法及名稱空間

一、面向物件 1、什麼是面向物件? 面向物件是一種思想,面向物件是將功能等通過物件來實現,將功能封裝進物件之中,讓物件去實現具體的細節,在面向物件中,將資料作為第一位,而方法或者說是演算法作為其次,這是對資料的一種優化,操作起來更加方便,簡化了過程。 2、為什麼要用面向

PowerDesigner物理數據表生成C#實體Model

valid pro return == 實體 mode obj tails 如果 model實體類是什麽: 在三層架構UI,BLL,DAL中,有時用戶插入一條記錄到數據庫中,必然會有不少數據,按正常編程,也必然會一下子調用某個函數傳入不少參數。為了減少參數的數量,達到高效

C++三種呼叫的複製建構函式的情況

用類的一個物件初始化同類的另一個物件時。 某函式的返回值是類的物件,呼叫該函式時。 某函式的形參是類的物件,呼叫該函式時。 ※注意區分“初始化”和“賦值”: ClassName c2 = c1;    (初始化語句) ClassName c1 , c2;

騰訊研發筆試面試試題(C++方向)

C的記憶體基本上分為4部分:靜態儲存區、堆區、棧區以及常量區。他們的功能不同,對他們使用方式也就不同。 1.棧 ——由編譯器自動分配釋放; 2.堆 ——一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收; 3.全域性區(靜態區)——全域性變數

讓你不再害怕指針——C指針詳解(經典,非常詳細)

有一個 情況 value 第一個字符 接下來 意思 strcpy abcdefg 數值 前言:復雜類型說明 要了解指針,多多少少會出現一些比較復雜的類型,所以我先介紹一下如何完全理解一個復雜類型,要理解復雜類型其實很簡單,一個類型裏會出現很多運算符,他們也像普通的表

JAVA關於java中 .class.getResource("/").getPath()獲取路徑有空格的問題

() 獲取路徑 return url fig net java.net nbsp 相關信息 寫了一個web工程,在本地測試正確,但是部署到服務器上就出現錯誤。原因是讀取不到配置文件。 後來從打印出來的文件路徑中發現是用Java的class.getResource("/").

Python面向對象--的特殊成員方法

運行 turn 中一 new 返回值 析構 school pytho comm 類的特殊成員方法 1. __doc__  表示類的描述信息 class Func(object): ‘‘‘__doc__方法是用來打印類的描述信息‘‘‘ def te