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用於除錯的輔助功能

留言

熱門文章