1. 程式人生 > >Lambda表示式以及在QT5訊號槽函式connect中的應用

Lambda表示式以及在QT5訊號槽函式connect中的應用

Lambda基本語法

簡單來說,Lambda函式也就是一個匿名函式,類似於object -c裡面的一個程式碼塊block語法,能夠在呼叫語句後面馬上列出要執行的程式碼,不需要預先宣告。它的語法定義如下:

[capture](parameters) mutable->return-type{statement} 

1.[capture]:捕捉列表。捕捉列表總是出現在Lambda函式的開始處。實際上,[]這對方括號被稱為Lambda表達 式引入符。捕捉列表能夠捕捉上下文中的變數以供Lambda函式使用;

2.(parameters):引數列表。與普通函式的引數列表一致。如果不需要引數傳遞,則可以連同括號“()”一起省略;

3.mutable:mutable修飾符。預設情況下,Lambda函式總是一個const函式,mutable可以取消其常量性。在使用該修飾符時,引數列表不可省略(即使引數為空);

4.->return-type:返回型別。用追蹤返回型別形式宣告函式的返回型別。我們可以在不需要返回值的時候也可以連同符號”->”一起省略。此外,在返回型別明確的情況下,也可以省略該部分,讓編譯器對返回型別進行推導;

5.{statement}:函式體。內容與普通函式一樣,不過除了可以使用引數之外,還可以使用所有捕獲的變數。

與普通函式最大的區別是,除了可以使用引數以外,Lambda函式還可以通過捕獲列表訪問一些上下文中的資料。具體地,捕捉列表描述了上下文中哪些資料可以被Lambda使用,以及使用方式(以值傳遞的方式或引用傳遞的方式)。語法上,在“[]”包括起來的是捕捉列表,捕捉列表由多個捕捉項組成,並以逗號分隔。捕捉列表有以下幾種形式:

1.[var]表示值傳遞方式捕捉變數var;2.[=]表示值傳遞方式捕捉所有父作用域的變數(包括this);3.[&var]表示引用傳遞捕捉變數var;4.[&]表示引用傳遞方式捕捉所有父作用域的變數(包括this);5.[this]表示值傳遞方式捕捉當前的this指標。

上面提到了一個父作用域,也就是包含Lambda函式的語句塊,說通俗點就是包含Lambda表示式的那段程式碼塊。上面的捕捉列表還可以進行組合,例如:

1.[=,&a,&b]表示以引用傳遞的方式捕捉變數a和b,以值傳遞方式捕捉其它所有變數;2.[&,a,this]表示以值傳遞的方式捕捉變數a和this,引用傳遞方式捕捉其它所有變數。

不過值得注意的是,捕捉列表不允許變數重複傳遞。下面一些例子就是典型的重複,會導致編譯時期的錯誤。例如:

3.[=,a]這裡已經以值傳遞方式捕捉了所有變數,但是重複捕捉a了,會報錯的;

4.[&,&this]這裡&已經以引用傳遞方式捕捉了所有變數,再捕捉this也是一種重複。

在QT5 connect函式中的應用:

我們都知道Qt5中允許訊號和槽的引數數目不一致:槽函式的引數數目可以比訊號的引數少。這是因為,我們訊號的引數實際是作為一種返回值。正如普通的函式呼叫一樣,我們可以選擇忽略函式返回值,但是不能使用一個並不存在的返回值。如果槽函式的引數數目比訊號的多,在槽函式中就使用到這些引數的時候,實際這些引數並不存在(因為訊號的引數比槽的少,因此並沒有傳過來),函式就會報錯。

然而,有一種情況,槽函式的引數可以比訊號的多,那就是槽函式的引數帶有預設值。比如,我們的Newspaper和Reader有下面的程式碼:

// Newspaper
signals:
    void new Paper(const QString& name);
// Reader
    void receiveNewspaper(const QString& name,const QDate& date=QDate::currentDate());

雖然Reader::receiveNewspaper()的引數數目比Newspaper::newPaper()多,但是由於Reader::receiveNewspaper()後面一個引數帶有預設值,

所以該引數不是必須提供的。但是,如果你按照下面的寫法,比如如下的程式碼:

connect(&newspaper,
        static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
        &reader,
        static_cast<void (Reader:: *)(const QString &, const QDate & = QDate::curentDate())>(&Reader::receiveNewspper));

你會得到一個斷言錯誤:The slot requires more arguments than the signal provides.

我們不能在函式指標中使用函式引數的預設值。這是 C++ 語言的限制:引數預設值只能使用在直接地函式呼叫中。當使用函式指標取其地址的時候,預設引數是不可見的!
當然,此時你可以選擇 Qt 4 的連線語法。如果你還是想使用 Qt 5 的新語法,目前的辦法只有一個:Lambda 表示式。

於是,我們的程式碼就變成了:

connect(&newspaper,
        static_cast<void(Newspaper::*)(const QString&)>(&Newspaper::newPaper),
         [=](const QString& name){/* Your code here. */});
此時訊號發出後沒有接收者,只執行Lambda表示式這個匿名函式裡的操作。這也是connect函式的一個過載,最後一個引數是 Functor 型別。
這個型別可以接受 static 函式、全域性函式以及 Lambda 表示式。