C++自修入門實境秀、C++ Primer 5版研讀秀 36 ~v6函式-6.5.2. Inline and constexpr Functions
頁236
6.5. Features for Specialized Uses
6.5特殊用途的功能
4:00
default arguments, inline and constexpr functions, and some facilities that are often used during debugging.
預設引數(default arguments)、行內(inline)和constexpr函式,以及經常會在除錯時使用的一些機能。
7:50
6.5.1. Default Arguments
預設引數
common value
預設引數是在宣告函式時定義的:
declare that common value as a default argument for the function.藉由宣告一個通用的值來作為一個函式的預設引數
我們可以將那個常用的值宣告為該函式的一個預設引數(default argument)。
其實就類似VBA裡頭的選擇性參數/引數,是在呼叫函式時可以省略的:
Functions with default arguments can be called with or without that argument.
accommodate
使用者有輸入則用使用者傳遞的引數,沒有則用預設引數
所以 在宣告函式參數中有加「=」的就是預設引數的寫法:
typedef string::size_type sz; // typedef see § 2.5.1 (p. 67)
string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');
一般函式的宣告是沒有「=」以後的文字的
就是沒有「初始器(初始設定式(initializer))」
可見預設引數的意思就是參數的預設值:
However, if a parameter has a default argument,
只要一個參數有了預設值(預設引數)那麼接在它後面的參數也都須設定其預設引數:
all the parameters that follow it must also have default argments.
31:30也就是預設引數(default argument)一定時在參數列的末端(尾端)
The default arguments are used for the
trailing (right-most) arguments of a call. For example, to override the default for background, we must also supply arguments for height and width:
pdf版與Google Play圖書的版本不同:
The default arguments are used for the trailing (right-most) arguments of a call. For example, to override the default for backgrnd , we must also supply arguments for ht and wid :
呼叫中的引數是以位置來解析的。預設引數用於一個呼叫尾端(最右方)的引數。舉例來說, 要覆寫backgrnd的預設值,我們也必須為ht和wid提供引數:
可見中文版是用Google Play圖書此版的來翻譯
Calling Functions with Default Arguments
叫喚(呼叫、調用)一個帶有預設引數的函式
以預設引數呼叫函式
screen(, , '?'); // error: can omit only trailing arguments
只有尾端的預設引數可以被省略。因此:
screen('?'); 就等同於screen('?',80,' ')
尾端的被省略了,所以自動填上預設值(自動帶上預設引數)
44:20
頁237
52:00
Part of the work of designing a function with default arguments 設計一個帶有預設引數的函式的一部分(首要,不可少的部分)工作,is 就是ordering the parameters 將函式的參數作排序,so that 讓those 那些 least likely 最不可能 to use a default value 用到預設值的(參數) appear first 排在前面,and 而 those 那些 most likely to use a default appear last.最可能用到預設值的排在最後
設計具有預設引數的函式時,有部分的工作就是排列參數的順序,讓那些最不可能使用預設值的參數先出現,而那些最可能使用預設值的參數最後出現。
在定義函式時,參數是沒有一定的排序規則,然而若是涉及預設引數(default argument)或所謂的選擇性參數時,就必須如上述考量各參數間的前後位置了。
1:1:30
Default Argument Declarations
預設引數宣告
1:4:30
重新宣告 redeclare
雖然函式可以重新(或重覆)宣告,但在同一範疇(scope)內,一旦其中一個參數已經設定了預設值,那麼後來重新或重覆宣告的函式,就不能再賦予該參數另一個預設值。意即參數預設值一經宣告,沒有離開範疇,即不能再更動,猶如static或const一般。
又一般也是要右方的參數已宣告了預設值,左方的才能宣告。有此順序。
1:16:30
Best Practices:
Default arguments ordinarily should be specified with the function declaration in an appropriate header.
預設引數一般應該與函式宣吿一起在適當的標頭中指定。
1:17:50
Default Argument Initializers
預設引數初始器
1:24:30
參數名稱也省略了:
string screen(sz = ht(), sz = wd, char = def);
只留下參數型別與初始值(預設值)
頁238
練習6.40
1:37:39
1:44:30
//(a)
int ff(int a, int b = 0, int c = 0);//it's correct. default arguments are at the most right
//(b) Error(active) E0306 default argument not at end of parameter list
char* init(int ht = 24, int wd, char bckgrnd);//should be
char* init( int wd, char bckgrnd, int ht = 24);
練習6.41
//(a)
//init();//Error (active) E0165 too few arguments in function call ,should be:
init(2);
1:49:00
//(b)OK
init(24, 10);
//(c)
//init(14, '*');//'*'對應到的是wd,會被整數提升(integral promotion)成int值,這就不是預期想要的了 should be:
init(14, 80,'*');
練習6.42
1:53:40
Exercise 6.42: Give the second parameter of make_plural (§ 6.3.2, p.224) a default argument of 's'. Test your program by printing singular and plural versions of the words success and failure.
練習6.42:賦予make_plural ( §6.3.2)的第二個參數一個's'的預設引數。印出字詞 success和failure的單數與複數版本來測試你的程式。
→印出success和failure這兩個英文字的單數與複數形態來測試你的程式。
這就更證明了作「第二個」是錯的,怎麼可能加複數形在英文字的本身上頭呢?一定是加在字的末綴嘛。所以它「第二個」參數才叫「word」,指英文字本身。而ending這個參數的意思就是word的末綴。
這裡「version」要翻成「形態」或「型態」才符合中文語境!怎麼可以照原文字面上翻成「版本」?不是行內人,誰看得懂?可見國文程度多重要!不是光外文能力強就可以勝任翻譯的工作了
中文版又與pdf版同,而與Google Play圖書異:
Exercise 6.42: Give the third parameter of make_plural (§ 6.3.2 , p. 224 ) a default argument of 's' . Test your program by printing singular and plural versions of the words success and failure .
中文版在頁224 make_plural的也是帶3個參數,所以這裡以「third」才對。因為預設引數(default argument)是要由末(尾端)到前頭,不可從中間,而接著沒有預設引數。所以怎麼可以是第2個,而讓第3個引數沒有預設值呢。可見Google Play圖書的版本應是後修訂的。而中文版在翻譯時,全然沒有用心來參與書中的討論與操作,只是照文字去做翻譯。
2:3:59
plural是英文單數複數的複數
singular是單數
// return the plural version of word if ctr is greater than 1
string make_plural(size_t ctr, const string& word,
const string& ending="s")
{
return (ctr > 1) ? word + ending : word;
}
int main() {
//傳回success、failure二字的單數形態
cout << "單數:"<<make_plural(1, "success", "") << endl;
cout << "單數:" << make_plural(1, "failure","") << endl;
//傳回success、failure二字的複數形態
cout << "複數:" << make_plural(2, "success","es") << endl;
cout << "複數:" << make_plural(2, "failure") << endl;//因為failure的複數型是+s而success的複數型則是es
}
印出來的是:
單數:success
單數:failure
複數:successes
複數:failures
2:46:20
6.5.2. Inline and constexpr Functions
6.5.2行內與constexpr函式
剛剛才提到「行內人」它就來個「行內」 哈哈
The benefits of defining a function for such a small operation include the following:寫下這麼小的函式有以下幾點好處
為這樣的小型運算定義一個函式的好處包括:
⑦先抓動詞 include ⑧找對主詞 benefits 因為include是複數型的動詞 我們這裡又翻得不太對,有點 ④倒序重組 篡改了原文了。
小型函式(可用微電腦、微電影、微博之例來取名為「微函式」。用「微」來翻「such a smaill」)的優長:
這種微函式的優點在於:(寫一個「函式」和寫一組「運算式」的較量。——其實函式通常就是一組運算式的集合(block) compound statement Synonym for block 見前)
1.它會比一個對等的條件運算式來得容易讀懂與理解
2.對函式的使用,可以確保操作行為及其效果的一致。
3.要做更動也較對運算式們更容易集中管理。
4.可以重複利用,甚至也可以給其他的應用程式套用。重複利用也比重寫一個好。
言外之意好像盡可能地用函式,而不要僅只是用述句或運算式來作操作。
估算一個運算式的時間,要比調用一個函式要短
在大部分的機器上,調用一個函式是要做很多事的:
暫存器 (registers)
1. 暫存器 (registers)的調用和恢復
2. 引數的拷貝和傳遞
3. 應用程式分工
inline Functions Avoid Function Call Overhead
inline函式避免了函式呼叫的負擔
可見inline函式是對微函式的增益
inline函式並不是微函式
inline顧名思義就是編譯器在一行中將微函式解開來成為一行類運算式,所以在執行期間,就不會把這樣的函式當作函式來處理(呼叫)
在本例中是把return的述句獨立「寫」出來而已;而不包括return此字。
Overhead
應該也有指函式「標頭」的意思,即函式介面 function's interface.。
inline就是類似把函式的介面(頭head)拿掉,只留下函式的本體 5:9:00
定義一個行內函式(inline function)是藉由在要作為行內函式的函式傳回型別前安置一個「inline」關鍵字來指定,就可以了
3:30:30
頁239
In general, the inline mechanism is meant to optimize small, straight-line functions that are called frequently. Many compilers will not inline a recursive function. A 75-line function will almost surely not be expanded inline.
一般來說,inline機制是為了最佳化經常被呼叫的小型直行函式而存在。
inline只是請求(刷卡),編譯器未必會買單。太大的函式(課本是作75行)或遞歸(recursion)的,都不會接受inline的申請
constexpr Functions
常值運算式(constant expression)函式
constexpr 函式
3:39:20
可用在常值運算式(constant expression)中的函式即是:
A constexpr function is a function that can be used in a constant expression (§ 2.4.4 , p. 65 ). A
constexpr函式的傳回型別及參數型別都得是字面值(literal)型別,且一定要剛好有一個return述句
編譯器對constexpr函式的處理就是(在編譯期間)將對constexpr函式呼叫直接轉換為它回傳的字面值(literal),以加速度調用它的處理。
In order to be able to expand the function immediately, constexpr functions are implicitly inline .
它是隱含(默認)的inline函式
既然定義時傳回的值必須是字面值,而constexpr函式卻又可以回傳一個非常值?
A constexpr function is permitted to return a value that is not a constant:
常值(constant)就是在編譯期間編譯器可以推估出來的值
4:14:00在本書中的scale函式如果算是constexpr函式,那麼constexpr函式就不必都是用字面值來定義return與參數型別了
4:20:00 原來「字面值型別」不等於「字面值(literal)」:
能在一個constexpr中使用的型別被稱作「字面值型別(literal types)」(頁66)
只要能在constexpr中使用的型別都是
在我們目前為止用過的型別中,算術、參考,以及指標型別都是字面值型別。
連參考、指標都是。
我們的Sales_item類別和程式庫的IO與string型別不是字面值型別。因此,我們不能將那種型別的變數定義為constexpr。
4:33:00本書應該至少要看上兩遍才能真正「上手」入心。
4:9:10
頁240
4:18:00
Note
A constexpr function is not required to return a constant expression.
不必然回傳的是常值運算式
Put inline and constexpr Functions in Header Files
將inline和constexpr函式放在標頭檔中
Unlike other functions, inline and constexpr functions may be defined multiple times in the program. ⑧找對主詞 may的主詞是inline and constexpr functions
inline和constexpr函式會被定義好幾次,而所有的定義必須完全一致:
all of the definitions of a given inline or constexpr must match exactly.
也因此才須定義在標頭檔中,以便控管。
4:44:10
練習6.43
程式碼該放在標頭檔(header)還是源碼檔(source file)?
(a) inline bool eq(const BigInt&, const BigInt&) {...}
inline函式宜放在標頭檔中,才好讓編譯器即時展開它們
(b) void putValues(int *arr, int size);
這是一般的函式,放在源碼檔——原始碼檔案(source file)即可。
練習6.44
4:49:55 4:58:20
//定義一個行內函式(inline function)是藉由在要作為行內函式的函式傳回型別前安置一個「inline」關鍵字來指定,就可以了
inline bool isShorter(const string& s1, const string& s2)
{
return s1.size() < s2.size();
}
int main() {
cout<<isShorter("好好念佛,成佛是大事,其他啥都是假的啊", "不畏浮雲遮望眼,自緣身在最高層")<<endl;
}
結果:印出「0」
練習6.45
5:3:30
inline void absv(int ab)//練習6.5
{
unsigned r = 1;
if (ab < 0)
{
r = ab * -1;
}
else
{
r = ab;
}
cout << r << endl;
return;
}
練習6.46
5:16:47
其參數型別為string& ,而參考是一個字面值(literal)型別;但是:
Severity Code Description
Error (active) E2392 a constexpr function cannot have a parameter of nonliteral type "std::string"
拿掉參考才會出錯:
Severity Code Description
Error C3615 constexpr function 'isShorter' cannot result in a constant expression
傳回不是常值,就是本書中所說的
一個constexpr函式能夠回傳不是常數的一個值(頁239)
可能須用char來改寫。string.size()比較大小傳回的也不會是constant
無法寫成constexpr的原因應是:
一個constexpr函式的主體可以含有其他的述句,只要那些述句不會在執行時期產生任何動作就行。舉例來說,一個constexpr函式可以含有null述句、型別別名(§ 2.5.1 ),或是 using宣告。(頁239)
而string.size()是會在執行階段做出動作的。
6.5.3. Aids for Debugging
6.5.3用於除錯的輔助功能
留言