日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Qt匿名函数的写法

發布時間:2023/12/18 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt匿名函数的写法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

匿名函數也可以被叫做Lambda表達式,自C++11中引入該特性。本文主要介紹Qt里使用到的匿名函數。

c11新特性中加入了lambda表達式,所以Qt 也支持

需在.pro文件中加入

CONFIG?+=?c++11

1. connect中使用

  • connect中可以使用匿名函數代替槽函數進行一些簡單操作。
  • 原型

    1 2 3 4 5 6 7 //connect to a functor template <typename Func1, typename Func2> static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::typeconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot) {return connect(sender, signal, sender, std::move(slot), Qt::DirectConnection); }
  • 示例

    1 2 3 4 QPushButton *button = new QPushButton; connect(button, &QPushButton::clicked, [this]() {... });

2. 排序使用

  • 原型

    1 2 3 4 5 6 template <typename RandomAccessIterator, typename LessThan> QT_DEPRECATED_X("Use std::sort") inline void qSort(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan) {if (start != end)QAlgorithmsPrivate::qSortHelper(start, end, *start, lessThan); }
  • 使用

    1 2 3 4 5 6 7 QList<int> list{3, 1, 2}; qSort(list.begin(), list.end(), [](int left, int right)->bool {return left < right;});

3. 高級線程中使用

  • QtConcurrent命名空間中的run接口支持匿名函數,用起來簡直爽得不要不要的。
  • 原型

    1 2 3 4 5 template <typename T> QFuture<T> run(T (*functionPointer)()) {return (new StoredFunctorCall0<T, T (*)()>(functionPointer))->start(); }
  • 使用

    1 2 3 QtConcurrent::run([=](){... });

4. 定時器中使用

  • QTimer的singleShot也支持匿名函數,用起來直觀明了。
  • 原型

    1 void singleShot(int msec, Functor functor)
  • 使用

    1 2 3 QTimer::singleShot(1000, [](){qDebug()<<"Finished!!!"; });

5. 與QVariant結合使用

  • 這個功能基本不會用到,楞是要找出一個用法可以看下Qt君往期推送的Qt網絡開源庫系列篇中有用到。
    1 2 3 4 5 6 7 8 9 10 11 Q_DECLARE_METATYPE(std::function<void ()>)int main(int argc, char *argv[]) {std::function<void ()> f = [](){ qDebug()<<"22"; };QVariant var = QVariant::fromValue(f);if (var.canConvert<std::function<void ()> >()) {std::function<void ()> func = var.value<std::function<void ()>>();func();} }

6. std::for_each

  • 原型

    1 2 3 4 5 6 7 8 9 template<class InputIterator, class Function> Function for_each(InputIterator first, InputIterator last, Function fn) {while (first!=last) {fn (*first);++first;}return fn; // or, since C++11: return move(fn); }
  • 示例

    1 2 3 4 5 6 QList<int> list{1, 2, 3}; std::for_each(list.begin(), list.end(), [](int i) { ...});

/********************************

例子:

QString program = "C:/Windows/System32/cmd.exe";QStringList arguments;arguments << "/c" << "dir" << "C:\\";QProcess* cmdProcess = new QProcess;QObject::connect(cmdProcess, &QProcess::readyRead,[=](){QTextCodec *codec = QTextCodec::codecForName("GBK");QString dir = codec->toUnicode(cmdProcess->readAll());qDebug() << dir;});cmdProcess->start(program, arguments);

一段簡單的Code

我也不是文藝的人,對于Lambda的歷史,以及Lambda與C++的那段淵源,我也不是很熟悉,技術人,講究拿代碼說事。

?代碼如下:

#include<iostream> using namespace std;int main() { int a = 1; int b = 2;auto func = [=, &b](int c)->int {return b += a + c;}; return 0; }

當我第一次看到這段代碼時,我直接凌亂了,直接看不懂啊。上面這段代碼,如果你看懂了,下面的內容就當時復習了;如果看不懂了,就接著和我一起總結吧。

基本語法

簡單來說,Lambda函數也就是一個函數,它的語法定義如下:


?代碼如下:

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

1.[capture]:捕捉列表。捕捉列表總是出現在Lambda函數的開始處。實際上,[]是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也是一種重復。

Lambda的使用

對于Lambda的使用,說實話,我沒有什么多說的,個人理解,在沒有Lambda之前的C++ , 我們也是那樣好好的使用,并沒有對缺少Lambda的C++有什么抱怨,而現在有了Lambda表達式,只是更多的方便了我們去寫代碼。不知道大家是否記得C++ STL庫中的仿函數對象,仿函數想對于普通函數來說,仿函數可以擁有初始化狀態,而這些初始化狀態是在聲明仿函數對象時,通過參數指定的,一般都是保存在仿函數對象的私有變量中;在C++中,對于要求具有狀態的函數,我們一般都是使用仿函數來實現,比如以下代碼:


代碼如下:

#include<iostream> using namespace std;typedef enum { add = 0, sub, mul, divi }type;class Calc { public: Calc(int x, int y):m_x(x), m_y(y){}int operator()(type i) { switch (i) { case add: return m_x + m_y; case sub: return m_x - m_y; case mul: return m_x * m_y; case divi: return m_x / m_y; } }private: int m_x; int m_y; };int main() { Calc addObj(10, 20); cout<<addObj(add)<<endl; // 發現C++11中,enum類型的使用也變了,更“強”了 return 0; }

現在我們有了Lambda這個利器,那是不是可以重寫上面的實現呢?看代碼:


代碼如下:

#include<iostream> using namespace std;typedef enum { add = 0, sub, mul, divi }type;int main() { int a = 10; int b = 20;auto func = [=](type i)->int { switch (i) { case add: return a + b; case sub: return a - b; case mul: return a * b; case divi: return a / b; } };cout<<func(add)<<endl; }

顯而易見的效果,代碼簡單了,你也少寫了一些代碼,也去試一試C++中的Lambda表達式吧。

關于Lambda那些奇葩的東西

看以下一段代碼:


代碼如下:

#include<iostream> using namespace std;int main() { int j = 10; auto by_val_lambda = [=]{ return j + 1; }; auto by_ref_lambda = [&]{ return j + 1; }; cout<<"by_val_lambda: "<<by_val_lambda()<<endl; cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;++j; cout<<"by_val_lambda: "<<by_val_lambda()<<endl; cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;return 0; }

程序輸出結果如下:


?代碼如下:

by_val_lambda: 11 by_ref_lambda: 11 by_val_lambda: 11 by_ref_lambda: 12

你想到了么???那這又是為什么呢?為什么第三個輸出不是12呢?

在by_val_lambda中,j被視為一個常量,一旦初始化后不會再改變(可以認為之后只是一個跟父作用域中j同名的常量),而在by_ref_lambda中,j仍然在使用父作用域中的值。所以,在使用Lambda函數的時候,如果需要捕捉的值成為Lambda函數的常量,我們通常會使用按值傳遞的方式捕捉;相反的,如果需要捕捉的值成成為Lambda函數運行時的變量,則應該采用按引用方式進行捕捉。

再來一段更暈的代碼:


代碼如下:

#include<iostream> using namespace std;int main() { int val = 0; // auto const_val_lambda = [=](){ val = 3; }; wrong!!!auto mutable_val_lambda = [=]() mutable{ val = 3; }; mutable_val_lambda(); cout<<val<<endl; // 0auto const_ref_lambda = [&]() { val = 4; }; const_ref_lambda(); cout<<val<<endl; // 4auto mutable_ref_lambda = [&]() mutable{ val = 5; }; mutable_ref_lambda(); cout<<val<<endl; // 5return 0; }

這段代碼主要是用來理解Lambda表達式中的mutable關鍵字的。默認情況下,Lambda函數總是一個const函數,mutable可以取消其常量性。按照規定,一個const的成員函數是不能在函數體內修改非靜態成員變量的值。例如上面的Lambda表達式可以看成以下仿函數代碼:


?代碼如下:

class const_val_lambda { public: const_val_lambda(int v) : val(v) {} void operator()() const { val = 3; } // 常量成員函數private: int val; };

對于const的成員函數,修改非靜態的成員變量,所以就出錯了。而對于引用的傳遞方式,并不會改變引用本身,而只會改變引用的值,因此就不會報錯了。都是一些糾結的規則。慢慢理解吧。

應用舉例

最近學習Qt,發現新大陸,這里做下記錄。

主要內容就是原始Qt4的信號槽連接方式,以及Qt5新版的連接方式,還有件事簡單演示一下lambda表達式的使用方式

代碼如下:

/** 簡述:該Demo僅僅用于測試和演示Qt5與Qt4的連接方式以及最新的槽函數支持lambda表達式*/#include "widget.h" #include "ui_widget.h" #include <QDebug> Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);// 傳統Qt是連接方式// 傳統Qt4連接方式為 信號發送者,信號,信號接受者,處理函數QObject::connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(qT4_slot()));//Qt5連接方式//其實這么寫的方式和Qt4沒有啥卻別,只是在Qt4 中引用了信號槽,在簡單的使用時沒有問題,但是在龐大的工程中,信號和曹 僅僅是宏替換,在編譯的時候沒有安全監測//Qt5的新方法,在編譯的時候就會有監測,如果我們手誤操作失誤,就會出現問題QObject::connect(ui->pushButton_2,&QPushButton::clicked,this,&Widget::qT5_slot);//Qt5 Lambda表達式//這里需要注意 Lambda表達式是C++ 11 的內容,所以,需要再Pro項目文件中加入 CONFIG += C++ 11QObject::connect(ui->pushButton_3,&QPushButton::clicked,[=](){qDebug()<<"lambda 表達式";});}Widget::~Widget() {delete ui; }void Widget::qT4_slot() {qDebug()<< "This is Qt 4 Connect method"; }void Widget::qT5_slot() {qDebug()<< "This is Qt 5 Connect method"; }

?

?

總結

以上是生活随笔為你收集整理的Qt匿名函数的写法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。