C++自修入門實境秀、C++ Primer 5版研讀秀 64/ ~ v10 泛用演算法(generic algorithms)10.3.4. ...





Capture by Value

捕捉其值(Capture by Value )

以值來捕捉來捕捉變數值

就像參數(引數)的傳遞,我們也可以藉由值或址的方式來(by value or by reference)捕捉一個變數

捕捉串列還是像參數列(parameter list)的功能嘛。

傳值(pass by value)就是copy拷貝

不一樣的地方在:

Unlike parameters, the value of a captured variable is copied when the lambda is created, not when it is called:

那麼就是在編譯期間就已經捕捉到了?

void fcn1() {

size_t v1 = 42; // local variable

// copies v1 into the callable object named f

auto f = [v1] { return v1; };

v1 = 0;

auto j = f(); // j is 42; f stored a copy of v1 when we created it

}

j並不是 f的拷貝,即j不是lambda,而是f的回傳型別與回傳其值

此v1是lambda f 的資料成員(data member)

此v1才是fcn1的資料成員(data member)(區域變數) 49:00

因為係拷貝copy才會各不相干

表10.1(頁395)列出了構成lambda捕捉串列(capture list)的各種方式

29:40

52:30 在 C語言中 「=」稱為「等號」或「等於的符號」,不要叫「等於」。「==」才是「等於」

號(符號)就是所謂的運算子啦

頁393

Capture by Reference

傳址(pass by reference)

一樣可以定義lambda去用參考(傳址)方式來捕捉在它外面的區域變數

void fcn2()

{

size_t v1 = 42; // local variable

// the object f2 contains a reference to v1

auto f2 = [&v1] { return v1; };

v1 = 0;

auto j = f2(); // j is 0; f2 refers to v1; it doesn't store it

}

在名稱前的「&」就表明是要用參考來捕捉外在區域變數的



15:00

參考捕捉跟參考回傳(reference returns,§6.3.2)有同樣的問題和限制。

→參考式的捕捉跟參考式的回傳

→參考型的捕捉跟參考型的回傳

要確保參考參照的物件是存在、有效的。問題就同迭代器(iterator)不可指向不存在的元素

18:00

cease to exist

停止存在、終止存在、存在結束→不復存在

字典cease哪有「不復」的意思?

翻譯學。可見翻譯決定不能一味地拘泥、迂腐在字典存在的字義上,字典上不收的字義就不敢去引申、類推、表哥原理地推闡、應用開來了!

1:3:40

void biggies(vector<string> &words,

vector<string>::size_type sz,

ostream &os = cout, char c = ' ')

{

// code to reorder words as before

// statement to print count revised to print to os

for_each(words.begin(), words.end(),

[&os, c](const string &s) { os << s << c; });

}

我們無法拷貝ostream物件(§ 8.1.1,頁311),捕捉os的唯一方法就是透過參考(或透過對os的一個指標)。

就知道為何c++除了物件本身,還要再加上複合型別(compound type)的指標(pointer)與參考(reference)。

1:11:12

也能從一個函式回傳lambda:

我們也能從一個函式回傳一個lambda。這個函式可以直接回傳一個可呼叫物件,或者函式可以回傳某個類別的一個物件,其中包含了一個可呼叫物件作為資料成員。

函式回傳一個可呼叫物件(callable object)或者是一個含有可呼叫物件(callable object)為其資料成員(data member)的物件

如果要回傳一個lambda 這個函式要保證這個要回傳回來的lambda沒有捕捉到一個區域變數的參考,因為其區域變數是此函式回傳後,一定會失效(已超出其生命週期、範疇)

頁394

當我們藉由參考捕捉一個變數,我們必須確保那個變數在lambda執行時有存在。

有存在→仍然存在

剛才才說不能拘泥在字典字典上來做翻譯!

忠告:Advice: Keep Your Lambda Captures Simple

捕捉串列(capture list)也可以翻成擷取串列、捕取串列捕捉取得也可



A lambda capture stores information between the time the lambda is created (i.e., when the code that defines the lambda is executed) and the time (or times) the lambda itself is executed. It is the programmer’s responsibility to ensure that whatever information is captured has the intended meaning each time the lambda is executed.

has the intended meaning(有意義)可見undefined有時就當翻成這個意思——沒有意義!那時候defined的意思就是「intended meaning」。

Capturing an ordinary variable—an int, a string, or other nonpointer type—by value is usually straightforward.

藉由傳值的方式來擷取(捕捉)一個普通的變數,是很直觀的做法。

In this case,在這種情況下 we only need to care whether the variable has the value we need when we capture it.

我們只需要去留意當我們在擷取這個變數時,它是否有我們當時想要的值

If we capture a pointer or iterator, or capture a variable by reference, we must ensure that the object bound to that iterator, pointer, or reference still exists, whenever the lambda executes.

而當我們想要藉由參考來捕捉一個變數的值,或想要取得一個指標或迭代器,我們就必須確定它們是有效的!它們所指涉的東西是存在的。

只要lambda在執行時,我們若是類似上面的做法,也一定要保證這些指標、迭代器、對區域變數的參考的有效性

Moreover, we need to ensure that the object has the intended value.

甚至,我們必須確保它們都有我們所預期的值

這些值都可能會在每次執行後變動,我們必須時時注意。不要待我們要取用時卻擷取到了不該存在的值或得到了不可預期的後果。因此若能遵守下列潛規則:

As a rule, we can avoid potential problems with captures by minimizing the data we capture. Moreover, if possible, avoid capturing pointers or references.

我們就能避免掉不必要的風險:當我們在定義lambda時,我們就該盡可能地減少對捕捉的需求。甚至,可以的話,避免去擷取像指標或參考、迭代器這樣傳址(可能就是所謂的複合型別(compound type)的物件)的東西。

作為一個好的翻譯,就是要讓讀者讀得輕鬆、易解,而且在閱讀的過程中,又能因茲學到一定程度的語文能力。 1:46:20

1:53:00甚至當一個lambda在擷取(捕捉)與執行時,如果是參考之類的型別(迭代器、指針)就可能取得的是不同的值:

The value of the object at the time the pointer (or reference) was captured might have been what we wanted. The value of that object when the lambda executes might be quite different.

what we wanted

我們 所 想要的

「所」就是類似what的代名詞嘛。可見學會了「所以」,人生真有大不同也!

capture在此情境(context上下文)中,可用擷取、取得……來抽換字面,不要呆呆地只會用一個字詞來表達、千篇一律



Implicit Captures

隱含的捕捉

不明說的擷取。

不待言傳的捕捉

不需要語文來表達的

隱性的vs explicit 顯性的

直接地捕捉

直接擷取

Rather than explicitly listing the variables we want to use from the enclosing function, we can let the compiler infer which variables we use from the code in the lambda’s body.

To direct the compiler to infer the capture list, we use an & or = in the capture list.

只在捕捉串列(capture list)使用「&」或「=」,就省略了條列那些本來該出現在那兒的區域變數,所以叫「隱含」

「&」就是傳址

「=」就是告訴編譯器傳值捕捉

// sz implicitly captured by value

wc = find_if(words.begin(), words.end(),

[=](const string &s) { return s.size() >= sz; });

如果我們想有些用傳址、有些用傳值,則我們可以用混合著implicit和explict的方式來組成捕捉串列(capture list)

要與Table 10.1. Lambda Capture List參照更明白

// os implicitly captured by reference; c explicitly captured by value

for_each(words.begin(), words.end(),

[&, c](const string &s) { os << s << c; });

// os explicitly captured by reference; c implicitly captured by value

for_each(words.begin(), words.end(),

[=, &os](const string &s) { os << s << c; });



Visual Studio Code格式化C++文本

1:02:00

Clang-Format要存檔後才能用

頁395

當我們混合隱含和明確捕捉,明確捕捉的變數必須使用替代的形式,

從上下文文義上來看「alternat」「Alternatively,」這裡應該翻成「交替的」「相對的」或「另一種」



[] 除了作為下標運算子([]運算子,subscript operator),也作為lambda的捕捉串列(capture list)標識用

Table 10.1. Lambda Capture List

2:39:40

表10.1 : Lambda的捕捉串列

[] 空的捕捉串列。這個lambda不可以使用來自外圍函式的變數。一個 lambda只能在捕捉了之後才能使用區域變數。

[names] names是由逗號分隔的名稱所成的一個串列,這些名稱是外圍函式的區 域名稱。預設情況下,捕捉串列中的變數會被拷貝。前面接著&的名 稱會以參考捕捉。

[&] 隱含是以參考進行的捕捉串列。用在lambda主體中來自外圍函式的實體是透過參考來使用的。

[=] 隱含是以值進行的捕捉串列。在lambda中用到的,來自外圍函式的實 體會被拷貝到lambda主體中。

[&,identifier_list] indentifier_list是來自外圍函式的零或更多個變數所成的逗號分隔串列。explicit 這些變數是以值來捕捉的,而隱含implicit捕捉的任何變數都是以參考捕捉的。 identifier_list中名稱的前面必須接著一個&。

[=,reference_list] 包含在reference_list中的變數是以參考捕捉的explicit,而任何隱含捕捉的變數都是以值捕捉的。reference_list中的名稱不能包含this,而且前面必須接著一個&。

&應該是「=」才是!英文版、中文版被均未正誤!

When we mix implicit and explicit captures, the first item in the capture list must be an & or = . That symbol sets the default capture mode as by reference or by value, respectively.

詳見前Implicit Captures處論述才明白



所以即使在捕捉串列中,前面接著「&」的名稱作會以參考來捕捉。因為在變數宣告時,參考型別標識符「&」也是冠在名稱之前,在型別名稱後。1:0:00

所以lambda的捕捉串列就像參數列一樣地捕捉引數,是傳址就用傳址(名稱前有&標識者),是傳值就用傳值

this這個特別的指標(pointer)也適用在lambda型別,因為它也要依其類別定義建構實例物件對象也。

1:22:00 在文件標題樣式的段落中尋找文字 2:2:00在書籤中找字串,並到書籤位置 2:05:00 在同一段中,找二個字串

Word VBA

1:43:00 關閉 dwm.exe 桌面視窗管理員



Mutable Lambdas

必可改動的lambda

mutable data member Data member that is never const(mutable data member (可變的資料成員))

可以改動捕捉的區域變數值的lambda是不能省略參數列(parameter list)的,且在其列之後要加上一個關鍵字mutable來標識之

void fcn3()



{



size_t vl = 42; // 區域變數 //f能夠變更它所捕捉的變數之值



auto f = [vl]() mutable { return ++vl; };

vl = 0;



auto j = f(); // j is 43

至於用參數方式來捕捉的區域變數,則看該參數是否是指向了一個const type:

Whether a variable captured by reference can be changed (as usual) depends only on whether that reference refers to a const or non const type:

depends only on翻成「只看」

void fcn4()

{

size_t v1 = 42; // 區域變數

//v1 是對一個非const變數的參考 //我們可以透過f2内的參考來變更那個變數 auto f2 = [&v1]{ return ++vl;};

vl = 0;

auto j = f2(); // j 是 1

}

頁396

Specifying the Lambda Return Type

指定lambda的回傳型別

3:0:00

3:0:50

body可翻成「軀幹」或「程式碼的部分」

若在lambda軀幹部分除了return述句,還有別的,而又沒指定回傳型別,那麼預設是不回傳值的(即傳回void型別)

3:25:00因此可以想像大概lambda預設的就是若它的本體有多於一個述句的內容,就是為了執行操作工作,而不是為了運算後回傳值。

transform演算法

3:3:50

用在replace數值上

帶四個引數的版本末尾引數是一個可呼叫物件(callable object,或predicate)即可用lambda表達式來表示

在在證明了演算法就是函式:

the library transform algorithm

The transform function

一下稱之演算法,一下又稱之函式

這是最明顯的例子了!



3:22:00

When we need to define a return type for a lambda, we must use a trailing return type (§ 6.3.3 , p. 229 ):

尾端回傳(trailing return)

3:32:45

頁397

練習10.20

count_if演算法

count_if returns a count of how often the predicate is true.

#include<vector>

#include<string>

#include <iostream>

#include <algorithm>

using namespace std;

int main() {

vector<string>vecs;

string word;

while (cin >> word)

{

vecs.push_back(word);

}

auto c=count_if(vecs.cbegin(), vecs.cend(),

[](const string& s)->bool {return s.size() > 6; });

cout << c << endl;

}



3:41:20

練習10.21

#include <iostream>

using namespace std;

int main() {

int i=111;

auto L = [&]()mutable->bool {

if(i!=0)--i; return i == 0; };//二個述句就必須有傳回型別

while (!L()) {}//不能少了呼叫運算子(call operator)

cout << i << endl;

L();

cout << i << endl;

}

3:56:20

10.3.4. Binding Arguments

10.3.4繫結引數

4:0:00是用lambda好還是用函式?

Lambda expressions are most useful for simple operations that we do not need to use in more than one or two places. If we need to do the same operation in many places, we should usually define a function rather than writing the same lambda expression multiple times多次.

一樣的,lambda的本體也不適合裝入太多的述句(運算式)

別忘了,lambda和行內函式(inline function)頗有關係。

雖然一般來講用函式取代空的捕捉串列(capture list)的lambda是簡單直捷的,但是若要寫一個函式來取代具有捕捉區域變數能力的lambda就不是同樣一回事了

4:10:10

The Library bind Function

程式庫的bind函式

程式庫函式library function

定義在functional 標頭檔

bind函式可以看作是一個通用的函式轉接器(adaptor,頁369)

它帶了一個可呼叫物件(callable object)作為引數,產生一個新的可呼叫物件,來調整、調適(adapts)原始物件的參數列。

頁398

auto newCallable = bind( callable , arg_list );

4:18:20

where newCallable is itself a callable object and arg_list is a comma-separated list of arguments that correspond to the parameters of the given callable .

when we call newCallable , newCallable calls callable , passing the arguments in arg_list .

4:23:10

The arguments in arg_list may include names of the form _ n ,「_n」這樣形式的參數名稱 where n is an integer. These arguments are “placeholders” representing the parameters of newCallable .

arg_list中的引數可以包括_n型式的名稱,其中n是一個整數。這些引數是「預留位置 (placeholders)」,代表newCallable的參數。

它們為將來會被傳入newCallable的引數「佔位置」。數字n是在所產生的callable(就是newCallable)中的參數位置:_1是newCallabe中的第一個參數,_2是第二個,依此類推。

Binding the sz Parameter of check_size

將check_size函式的sz參數(頁397)繫結給外部函式biggies定義的區域變數或參數sz(頁394)

⑧找對主詞

對bind的這個呼叫會產生一個可呼叫物件,它將check_size的第二個引數繫結到了 sz 的值。

bool check_size(const string &s, string::size_type sz) {

return s.size() >= sz;

}



void biggies(vector<string> &wordsf vector<string>::size_type sz

, ostream &os = cout, char c = ' ')

用 bind來產生一個物件,這個物件會用一個回定的值來作為初始化check_size函式的size這個參數值,來呼叫check_size函式

5:19:00

auto wc = find_if(words.begin(), words.end(), bind(check_size, _1, sz));

此三段程式碼可清晰看到三者交織(bind)在一起

This call to bind generates a callable object that binds the second argument of check_size to the value of sz .



placeholder 預留位置



5:8:00 bind繫結就好像臍帶一樣,連成一氣

連成一體。所以在單元判斷式(unary predicate)才能視為只有一個引數,因為另一個是被繫結了,不歸它判斷式來管。



5:12:00

Using bind , we can replace our original lambda-based call to find_if

-based可翻成以……來。∴

lambda-based=以lambda來



5:17:00

auto wc = find_if(words.begin(), words.end(),

[sz](const string &a)

頁390後面常有{return a.size()>=sz;};才是



頁399

Using placeholders Names

預留位置(placeholder)名稱本身就是有個底線「_」再加上整數n後綴形成「_n」

這個「_n」名稱是在命名空間placeholders中被定義的:

The _ n names are defined in a namespace named placeholders .

而這個命名空間又是在std命名空間裡頭定義的:

That namespace is itself defined inside the std namespace (§ 3.1 , p. 82 ).

要用「_1」這樣的,一定要在前有using宣告:

using std::placeholders::_1;

This declaration says we’re using the name _1 , which is defined in the namespace placeholders , which is itself defined in the namespace std .

由「;」可見都是一個述句。用「_2、_3、_4……」也須個個宣告

Rather than separately declaring each placeholder, we can use a different form of using that we will cover in more detail in § 18.2.2 (p. 793 ). This form:

using namespace namespace_name ;

拜託,這個我們已經用爛了 呵呵

says that we want to make all the names from namespace_name accessible to our program.

using namespace std::placeholders;

makes all the names defined by placeholders usable. Like the bind function, the placeholders namespace is defined in the functional header.

bind和預留位置(placeholder)都與函式有關,定義在funcional標頭檔中也是合理

5:45:59

Arguments to bind

bind的引數

bind函式的引數(又囿於字典字義文法結構了)

⑦先抓動詞 此bind是函式名,不是動詞!

引數arguments是用介係詞to

留言

熱門文章