function類模版
function
是一個模版肢预。與我們使用的其他模版一樣,當我們創(chuàng)建一個function
類型時我們必須指定額外的信息洼哎。在這種情況下烫映,該信息是該特定function
類型可以表示的對象的調(diào)用簽名。與其他模板一樣噩峦,我們在尖括號內(nèi)指定類型:
Code:
function<int(int, int)>
這里我們聲明了一個function
類型锭沟,它可以表示返回int
結(jié)果并具有兩個int
參數(shù)的可調(diào)用對象。我們可以使用該類型來表示任何桌面計算器類型:
Code:
function<int(int, int)> f1 = add; // function pointer
function<int(int, int)> f2 = div(); // object of a function-object class
function<int(int, int)> f3 = [](int i, int j) // lambda
{ return i * j; };
cout << f1(4,2) << endl; // prints 6
cout << f2(4,2) << endl; // prints 2
cout << f3(4,2) << endl; // prints 8
我們可以很容易地使用function
類模版和map
容器建立一個函數(shù)表(function table:容器识补,通常是map
或vector
族淮,包含可以調(diào)用的值,如函數(shù)凭涂。):
Code:
// table of callable objects corresponding to each binary operator
// all the callables must take two ints and return an int
// an element can be a function pointer, function object, or lambda
map<string, function<int(int, int)>> binops;
我們可以將每個可調(diào)用對象(無論是函數(shù)指針祝辣,lambda還是函數(shù)對象)添加到此map
:
Code:
map<string, function<int(int, int)>> binops = {
{"+", add}, // function pointer
{"-", std::minus<int>()}, // library function object
{"/", div()}, // user-defined function object
{"*", [](int i, int j) { return i * j; }}, // unnamed lambda
{"%", mod} }; // named lambda object
我們的map
有五個元素。盡管底層可調(diào)用對象的類型彼此不同切油,但是我們可以將這些不同的類型存儲在相同的function<int(int, int)>
類型中蝙斜。
像往常一樣,當我們索引map
時澎胡,我們得到對相關(guān)值的引用孕荠。當我們索引binops
時,我們得到一個類型為function
的對象的引用攻谁。函數(shù)類型會重載調(diào)用操作符稚伍。該調(diào)用運算符獲取自己的參數(shù)并將它們傳遞給其存儲的可調(diào)用對象:
Code:
binops["+"](10, 5); // calls add(10, 5)
binops["-"](10, 5); // uses the call operator of the minus<int> object
binops["/"](10, 5); // uses the call operator of the div object
binops["*"](10, 5); // calls the lambda function object
binops["%"](10, 5); // calls the lambda function object
這里我們調(diào)用存儲在binops
中的每個操作。在第一次調(diào)用中戚宦,我們返回的元素包含一個指向我們的add
函數(shù)的函數(shù)指針个曙。調(diào)用binops["+"](10, 5)
實際上是使用該指針調(diào)用add
,并將10和5傳遞給它阁苞。在下一個調(diào)用中binops["-"]
困檩,將返回一個存儲類型為std::minus<int>
的函數(shù)。我們稱之為對象的調(diào)用操作符那槽,依此類推。
我們不能(直接)將重載函數(shù)的名稱存儲在function
類型的對象中:
Code:
int add(int i, int j) { return i + j; }
Sales_data add(const Sales_data&, const Sales_data&);
map<string, function<int(int, int)>> binops;
binops.insert( {"+", add} ); // error: which add?
解決歧義的一種方法是存儲函數(shù)指針而不是函數(shù)的名稱:
Code:
int (*fp)(int,int) = add; // pointer to the version of add that takes two ints
binops.insert( {"+", fp} ); // ok: fp points to the right version of add
或者等舔,我們可以使用lambda消除歧義:
Code:
// ok: use a lambda to disambiguate which version of add we want to use
binops.insert( {"+", [](int a, int b) {return add(a, b);} } );
lambda主體內(nèi)部的調(diào)用傳遞兩個int
骚灸。該調(diào)用只能與接受兩個int
的add
版本匹配,因此這是在執(zhí)行l(wèi)ambda時調(diào)用的函數(shù)慌植。
新庫中的function
類與名為unary_function
和binary_function
的類無關(guān)甚牲,這些類是早期版本的庫的一部分义郑。這些類已被更通用的bind
函數(shù)棄用。
關(guān)于function
類模板的更多信息可參考std::function丈钙。
explicit轉(zhuǎn)換運算符
在實踐中非驮,類很少提供轉(zhuǎn)換運算符。實際上編譯器為我們提供的自動轉(zhuǎn)換很容易產(chǎn)生意想不到的結(jié)果雏赦。而在實際工作中劫笙,類定義到bool
的轉(zhuǎn)換并不少見。
在早期版本中星岗,想要定義到bool
的轉(zhuǎn)換的類面臨一個問題:因為bool
是算術(shù)類型填大,所以轉(zhuǎn)換為bool
的類類型對象可以在任何需要算術(shù)類型的上下文中使用。這種轉(zhuǎn)換可能以令人驚訝的方式發(fā)生俏橘。特別是允华,如果istream
有一個到bool
類型的轉(zhuǎn)換,則以下代碼將成功編譯:
Code:
int i = 42;
cin << i; // this code would be legal if the conversion to bool were not explicit!
該程序嘗試在輸入流上使用輸出運算符寥掐。沒有<<
為istream
定義靴寂,因此該代碼幾乎可以肯定是錯誤的。但是召耘,此代碼可以使用bool
轉(zhuǎn)換運算符將cin
轉(zhuǎn)換為bool
榨汤。然后,生成的bool
值將提升為int
怎茫,并用作內(nèi)置版本的左移運算符的左側(cè)操作數(shù)收壕。提升的bool
值(1或0)將向左移位42個位置。
為防止此類問題轨蛤,新標準引入了explicit
轉(zhuǎn)換運算符:
Code:
class SmallInt {
public:
// the compiler won't automatically apply this conversion
explicit operator int() const { return val; }
// other members as before
};
與explicit
構(gòu)造函數(shù)一樣蜜宪,編譯器不會使用explicit
轉(zhuǎn)換運算符進行隱式轉(zhuǎn)換:
Code:
SmallInt si = 3; // ok: the SmallInt constructor is not explicit
si + 3; // error: implicit is conversion required, but operator int is explicit
static_cast<int>(si) + 3; // ok: explicitly request the conversion
如果轉(zhuǎn)換運算符是explicit
的,我們?nèi)匀豢梢赃M行轉(zhuǎn)換祥山。但是圃验,我們必須通過一個強制轉(zhuǎn)換來顯式地這樣做。
例外情況是編譯器將對用作條件的表達式應(yīng)用顯式轉(zhuǎn)換缝呕。也就是說澳窑,將隱式使用顯式轉(zhuǎn)換來轉(zhuǎn)換用作:
-
if
,while
或do
語句的條件供常。 -
for
語句頭中的條件表達式摊聋。 - 邏輯NOT(
!
),OR(||
)或AND(&&
)運算符的操作數(shù)栈暇。 - 條件(
?:
)運算符中的條件表達式
在早期版本的庫中麻裁,IO類型定義了轉(zhuǎn)換為void *
。他們這樣做是為了避免上述各種問題。在新標準下煎源,IO庫定義了對bool
的explicit
轉(zhuǎn)換色迂。
每當我們在條件中使用流對象時,我們使用為IO類型定義的運算符bool
手销。 例如:
Code:
while (std::cin >> value)
while
中的條件執(zhí)行輸入操作符歇僧,該操作符讀入值并返回cin
。為了評估條件锋拖,cin
由istream operator bool
轉(zhuǎn)換函數(shù)隱式轉(zhuǎn)換诈悍。如果cin
的條件狀態(tài)為good
,則該函數(shù)返回true
姑隅,否則返回false
写隶。
注:轉(zhuǎn)換為bool
通常用于條件。因此讲仰,operator bool
通常應(yīng)該被定義為explicit
慕趴。
參考文獻
[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.