最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Lambda表達式里面修改外部變量問題

 更新時間:2020年03月24日 08:51:38   作者:WolfcsTech  
這篇文章主要介紹了Lambda表達式里面修改外部變量的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

先看下Lambda表達式里面修改外部變量問題

因為平臺用的是JDK8,而且發(fā)現(xiàn)自己對那些新特性好像一點都不了解,就例如Lambda表達式,所以就開始對自己的代碼進行改進了。。。

        例如遍歷Map,像我們正常遍歷肯定是下面這樣子的。

String result = "select * from where id = '#userId#' and name = '#userName#'";
Map<String,String> sysParams = new HashMap<String,String>();
sysParams.put("#userId#", "userId");
sysParams.put("#userName#", "userName");
sysParams.put("#realName#", "realName");
sysParams.put("#orgIds#", "orgIds");
sysParams.put("#departname#", "departname");
sysParams.put("#roleId#", "roleId");
 
for(Map.Entry<String, String> entry : sysParams.entrySet()){
 if(result .contains(entry.getKey())){
 result = result .replaceAll(entry.getKey(), AppDataUtils.replaceSysData(entry.getValue()));
 }
}

但是如果用Lambda表達式呢,將會非常的簡單,

但是我們會發(fā)現(xiàn),result那里是會報錯的:Local variable result defined in an enclosing scope must be final or effectively final

String result = "select * from where id = '#userId#' and name = '#userName#'";
Map<String,String> sysParams = new HashMap<String,String>();
sysParams.put("#userId#", "userId");
sysParams.put("#userName#", "userName");
sysParams.put("#realName#", "realName");
sysParams.put("#orgIds#", "orgIds");
sysParams.put("#departname#", "departname");
sysParams.put("#roleId#", "roleId");
 
sysParams.forEach((key,value)->{
 if(result.contains(key)){
 result = result.replaceAll(key, value);
  
 }
});

這是因為:Java會將result的值作為參數(shù)傳遞給Lambda表達式,為Lambda表達式建立一個副本,它的代碼訪問的是這個副本,而不是外部聲明result變量??赡芎芏嗤瑢W會問為什么非要建立副本呢,直接訪問外部的result變量得多方便呢。答案是:這是不可能滴,因為result定義在棧中,當Lambda表達式被執(zhí)行的時候,result可能已經被釋放掉了。

當然啦,你要是一定要在Lambda表達式里面修改外部變量的值也是可以的,可以將變量定義為實例變量或者將變量定義為數(shù)組。

下面我就改為數(shù)組咯,實例變量不方便。

String result = "select * from where id = '#userId#' and name = '#userName#'";
 //將變量定義為數(shù)組就好了
  String[] arr = new String[]{result};
 Map<String,String> sysParams = new HashMap<String,String>();
 sysParams.put("#userId#", "userId");
 sysParams.put("#userName#", "userName");
 sysParams.put("#realName#", "realName");
 sysParams.put("#orgIds#", "orgIds");
 sysParams.put("#departname#", "departname");
 sysParams.put("#roleId#", "roleId");
 
 sysParams.forEach((key,value)->{
 if(arr[0].contains(key)){
      //都是對數(shù)組進行操作了
  arr[0] = arr[0].replaceAll(key, value);
  
 }
 });

下面看下C++ lambda 捕獲模式與右值引用詳解

lambda 表達式和右值引用是 C++11 的兩個非常有用的特性。

lambda 表達式實際上會由編譯器創(chuàng)建一個 std::function 對象,以值的方式捕獲的變量則會由編譯器復制一份,在 std::function 對象中創(chuàng)建一個對應的類型相同的 const 成員變量,如下面的這段代碼:

int main(){
 std::string str = "test";
 printf("String address %p in main, str %s\n", &str, str.c_str());
 auto funca = [str]() {
 printf("String address %p (main lambda), str %s\n", &str, str.c_str());
 };
 std::function<void()> funcb = funca;
 std::function<void()> funcc;
 funcc = funca;
 printf("funca\n");
 funca();
 std::function<void()> funcd = std::move(funca);
 printf("funca\n");
 funca();
 printf("funcb\n");
 funcb();
 std::function<void()> funce;
 funce = std::move(funcb);
 printf("funcb\n");
// funcb();
 printf("funcc\n");
 funcc();
 printf("funcd\n");
 funcd();
 printf("funce\n");
 funce();
// std::function<void(int)> funcf = funce;
 return 0;
}

這段代碼的輸出如下:

Stringaddress0x7ffd9aaab720 in main, strtest
funca
Stringaddress0x7ffd9aaab740 (main lambda), strtest
funca
Stringaddress0x7ffd9aaab740 (main lambda), str
funcb
Stringaddress0x55bdd2160280 (main lambda), strtest
funcb
funcc
Stringaddress0x55bdd21602b0 (main lambda), strtest
funcd
Stringaddress0x55bdd21602e0 (main lambda), strtest
funce
Stringaddress0x55bdd2160280 (main lambda), strtest

由上面調用 funca 時的輸出,可以看到 lambda 表達式以值的方式捕獲的對象 str,其地址在 lambda 表達式內部和外部是不同的。

std::function 類對象和普通的魔板類對象一樣,可以拷貝構造,如:

std::function<void()> funcb = funca;

由調用 funcb 時的輸出,可以看到拷貝構造時是做了逐成員的拷貝構造。

std::function 類對象可以賦值,如:

std::function<void()> funcc;
funcc = funca;

由調用 funcc 時的輸出,可以看到賦值時是做了逐成員的賦值。

std::function 類對象可以移動構造,如:

std::function<void()> funcd = std::move(funca);

由移動構造之后,調用 funca 和 funcd 時的輸出,可以看到移動構造時是做了逐成員的移動構造。

std::function 類對象可以移動賦值,如:

 std::function<void()> funce;
 funce = std::move(funcb);

 printf("funcb\n");
// funcb();

這里把移動賦值之后對 funcb 的調用注釋掉了,這是因為,作為源的 funcb 在移動賦值之后被調用是,會拋出異常,如:

String address 0x562334c34280 (main lambda), str test
funcb
terminate called after throwing aninstanceof 'std::bad_function_call'
 what(): bad_function_call

同時,由調用 funce 時的輸出可以看到,該輸出與 funcb 在移動賦值之前被調用時的輸出完全相同。 即移動賦值是將對象整體 move 走了,這與移動構造時的行為不太一樣。

std::function 類對象的拷貝構造或者賦值,也需要滿足類型匹配原則,如:

std::function<void(int)> funcf = funce;

這行代碼會造成編譯失敗,編譯錯誤信息如下:

../src/DemoTest.cpp: In function ‘intmain()':
../src/DemoTest.cpp:64:36: error: conversion from ‘std::function<void()>' to non-scalar type ‘std::function<void(int)>' requested
   std::function<void(int)> funcf = funce;
                                    ^~~~~
make: *** [src/DemoTest.o] Error 1
src/subdir.mk:18: recipe for target 'src/DemoTest.o' failed

在 lambda 中以值的方式捕獲的右值對象,只是在 lambda 的 std::function 對象中做了一份被捕獲的右值對象的拷貝,而原來的右值則沒有任何改變。

接下來再來看一段示例代碼:

#include<iostream>
#include<functional>
#include<string>

using namespace std;

void funcd(std::string&&str){
 printf("String address %p in funcd A, str %s\n", &str, str.c_str());
 string strs = std::move(str);
 printf("String address %p in funcd B, str %s, strs %s\n", &str, str.c_str(), strs.c_str());
}

void funcc(std::stringstr){
 printf("String address %p in funcc, str %s\n", &str, str.c_str());
}

void funcb(std::string&str){
 printf("String address %p in funcb, str %s\n", &str, str.c_str());
}

void funca(std::string&&str){
 printf("String address %p in funca A, str %s\n", &str, str.c_str());
 std::string stra = str;
 printf("String address %p in funca B, str %s, stra %s\n", &str, str.c_str(), stra.c_str());
}

int main(){
 std::string str = "test";
 printf("String address %p in main A, str %s\n", &str, str.c_str());

 funca(std::move(str));
 printf("String address %p in main B, str %s\n", &str, str.c_str());

// funcb(std::move(str));
 printf("String address %p in main C, str %s\n", &str, str.c_str());

 funcc(std::move(str));
 printf("String address %p in main D, str %s\n", &str, str.c_str());

 std::string stra = "testa";
 printf("String address %p in main E, stra %s\n", &stra, stra.c_str());

 funcd(std::move(stra));
 printf("String address %p in main F, stra %s\n", &stra, stra.c_str());

 return 0;
}

上面這段代碼在執(zhí)行時,輸出如下:

String address 0x7ffc833f4660 in main A, str test
String address 0x7ffc833f4660 in funca A, str test
String address 0x7ffc833f4660 in funca B, str test, stra test
String address 0x7ffc833f4660 in main B, str test
String address 0x7ffc833f4660 in main C, str test
String address 0x7ffc833f4680 in funcc, str test
String address 0x7ffc833f4660 in main D, str
String address 0x7ffc833f4680 in main E, stra testa
String address 0x7ffc833f4680 in funcd A, str testa
String address 0x7ffc833f4680 in funcd B, str , strs testa
String address 0x7ffc833f4680 in main F, stra

funca 函數(shù)接收右值引用作為參數(shù),由 funca 函數(shù)內部及函數(shù)調用前后的輸出可以看到, std::move() 本身什么都沒做,單單調用 std::move() 并不會將原來的對象的內容移動到任何地方。 std::move() 只是一個簡單的強制類型轉換,將左值轉為右值引用。同時可以看到,用右值引用作為參數(shù)構造對象,也并沒有對右值引用所引用的對象產生任何影響。

funcb 函數(shù)接收左值引用作為參數(shù),上面的代碼中,如下這一行注釋掉了:

//  funcb(std::move(str));

這是因為, funcb 不能用一個右值引用作為參數(shù)來調用。用右值引用作為參數(shù),調用接收左值引用作為參數(shù)的函數(shù) funcb 時,會編譯失?。?/p>

g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/DemoTest.d" -MT"src/DemoTest.o" -o "src/DemoTest.o" "../src/DemoTest.cpp"
../src/DemoTest.cpp: In function ‘int main()':
../src/DemoTest.cpp:34:18: error: cannot bind non-const lvalue reference of type ‘std::__cxx11::string& {aka std::__cxx11::basic_string<char>&}' to an rvalue of type ‘std::remove_reference<std::__cxx11::basic_string<char>&>::type {aka std::__cxx11::basic_string<char>}'
   funcb(std::move(str));
         ~~~~~~~~~^~~~~
../src/DemoTest.cpp:17:6: note:   initializing argument 1 of ‘void funcb(std::__cxx11::string&)'
 void funcb(std::string &str) {
      ^~~~~
src/subdir.mk:18: recipe for target 'src/DemoTest.o' failed
make: *** [src/DemoTest.o] Error 1

不過,如果 funcb 接收 const 左值引用作為參數(shù),如 void funcb(const std::string &str) ,則在調用該函數(shù)時,可以用右值引用作為參數(shù),此時 funcb 的行為與 funca 基本相同。

funcc 函數(shù)接收左值作為參數(shù),由 funcc 函數(shù)內部及函數(shù)調用前后的輸出可以看到,由于有了左值作為接收者,傳入的右值引用所引用的對象的值被 move 走,進入函數(shù)的參數(shù)棧對象中了。

funcd 函數(shù)與 funca 函數(shù)一樣,接收右值引用作為參數(shù),但 funcd 的特別之處在于,在函數(shù)內部,右值構造了一個新的對象,因而右值引用原來引用的對象的值被 move 走,進入了新構造的對象中。

再來看一段示例代碼:

#include<iostream>
#include<functional>
#include<string>

using namespace std;

void bar(std::string&&str){
 printf("String address %p in bar A, str %s\n", &str, str.c_str());
 string strs = std::move(str);
 printf("String address %p in bar B, str %s, strs %s\n", &str, str.c_str(), strs.c_str());
}

std::function<void()> bar_bar(std::string &&str) {
 auto funf = [&str]() {
 printf("String address %p (foo lambda) F, stra %s\n", &str, str.c_str());
 };
 return funf;
}

std::function<void()> foo(std::string &&str) {
 printf("String address %p in foo A, str %s\n", &str, str.c_str());

// auto funa = [str]() {
// printf("String address %p (foo lambda) A, str %s\n", &str, str.c_str());
// bar(str);
// };
// funa();
//
// auto funb = [str]() {
// printf("String address %p (foo lambda) B, str %s\n", &str, str.c_str());
// bar(std::move(str));
// };
// funb();

// auto func = [str]() mutable {
// printf("String address %p (foo lambda) C, str %s\n", &str, str.c_str());
// bar(str);
// };
// func();

 auto fund = [str]() mutable {
 printf("String address %p (foo lambda) D, str %s\n", &str, str.c_str());
 bar(std::move(str));
 };
 fund();

 auto fune = [&str]() {
 printf("String address %p (foo lambda) E, str %s\n", &str, str.c_str());
 bar(std::move(str));
 };
 fune();

 std::string stra = "testa";
 return bar_bar(std::move(stra));
}

int main(){
 std::string str = "test";
 printf("String address %p in main A, str %s\n", &str, str.c_str());

 auto funcg = foo(std::move(str));
 printf("String address %p in main B, str %s\n", &str, str.c_str());

 funcg();

 return 0;
}

上面這段代碼的輸出如下:

Stringaddress0x7ffc9fe7c5c0 in main A, strtest
Stringaddress0x7ffc9fe7c5c0 in foo A, strtest
Stringaddress0x7ffc9fe7c540 (foo lambda) D, strtest
Stringaddress0x7ffc9fe7c540 in barA, strtest
Stringaddress0x7ffc9fe7c540 in barB,str, strstest
Stringaddress0x7ffc9fe7c5c0 (foo lambda) E, strtest
Stringaddress0x7ffc9fe7c5c0 in barA, strtest
Stringaddress0x7ffc9fe7c5c0 in barB,str, strstest
Stringaddress0x7ffc9fe7c5c0 in main B,str
Stringaddress0x7ffc9fe7c560 (foo lambda) F, stra����

在函數(shù) foo() 中定義的 funa 及對 funa 的調用被注釋掉了,這是因為這段代碼會導致編譯失敗,具體的錯誤信息如下:

Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/DemoTest.d" -MT"src/DemoTest.o" -o "src/DemoTest.o" "../src/DemoTest.cpp"
../src/DemoTest.cpp: In lambda function:
../src/DemoTest.cpp:25:12: error: cannot bind rvalue reference of type ‘std::__cxx11::string&& {aka std::__cxx11::basic_string<char>&&}' to lvalue of type ‘const string {aka const std::__cxx11::basic_string<char>}'
     bar(str);
            ^
../src/DemoTest.cpp:7:6: note:   initializing argument 1 of ‘void bar(std::__cxx11::string&&)'
 void bar(std::string &&str) {
      ^~~
src/subdir.mk:18: recipe for target 'src/DemoTest.o' failed
make: *** [src/DemoTest.o] Error 1

如我們前面提到的,在 lambda 表達式中,以值的方式捕獲右值引用時,會在編譯器為該 lambda 表達式生成的 std::function 類中生成一個 const 對象,const 對象是不能作為右值引用來調用接收右值引用為參數(shù)的函數(shù)的。

在函數(shù) foo() 中定義的 funb ,相對于 funa ,在調用 bar() 時,為 str 裹上了 std::move() 。不過此時還是會編譯失敗。錯誤信息如下:

Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/DemoTest.d" -MT"src/DemoTest.o" -o "src/DemoTest.o" "../src/DemoTest.cpp"
../src/DemoTest.cpp: In lambda function:
../src/DemoTest.cpp:31:18: error: binding reference of type ‘std::__cxx11::string&& {aka std::__cxx11::basic_string<char>&&}' to ‘std::remove_reference<const std::__cxx11::basic_string<char>&>::type {aka const std::__cxx11::basic_string<char>}' discards qualifiers
     bar(std::move(str));
         ~~~~~~~~~^~~~~
../src/DemoTest.cpp:7:6: note:   initializing argument 1 of ‘void bar(std::__cxx11::string&&)'
 void bar(std::string &&str) {
      ^~~
make: *** [src/DemoTest.o] Error 1
src/subdir.mk:18: recipe for target 'src/DemoTest.o' failed

在 funb 中, str 是個 const 對象,因而還是不行。

在函數(shù) foo() 中定義的 func ,相對于 funa ,加了 mutable 修飾。此時還是會編譯失敗。錯誤信息如下:

Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/DemoTest.d" -MT"src/DemoTest.o" -o "src/DemoTest.o" "../src/DemoTest.cpp"
../src/DemoTest.cpp: In lambda function:
../src/DemoTest.cpp:37:12: error: cannot bind rvalue reference of type ‘std::__cxx11::string&& {aka std::__cxx11::basic_string<char>&&}' to lvalue of type ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}'
     bar(str);
            ^
../src/DemoTest.cpp:7:6: note:   initializing argument 1 of ‘void bar(std::__cxx11::string&&)'
 void bar(std::string &&str) {
      ^~~
make: *** [src/DemoTest.o] Error 1
src/subdir.mk:18: recipe for target 'src/DemoTest.o' failed

無法將左值綁定到一個右值引用上。

在函數(shù) foo() 中定義的 fund ,相對于 func ,在調用 bar() 時,為 str 裹上了 std::move() 。此時終于可以編譯成功,可以 move const 的 str 。

在函數(shù) foo() 中定義的 fune ,相對于 funb ,以引用的方式捕獲了右值引用。在 fune 中調用 bar() ,就如同 foo() 直接調用 bar() 一樣。

在函數(shù) foo() 中調用接收一個右值引用作為參數(shù)的函數(shù) bar_bar() 生成一個函數(shù)。在函數(shù) bar_bar() 中用 lambda 定義的函數(shù)對象 funf ,以引用的方式捕獲一個右值,并在 lambda 中訪問改對象。該 lambda 作為 bar_bar() 函數(shù)生成的函數(shù)對象。 foo() 中調用 bar_bar() 時傳入函數(shù)棧上定義的臨時對象 stra ,并將 bar_bar() 返回的函數(shù)對象作為返回值返回。在 main() 函數(shù)中用 funcg 接收 foo() 函數(shù)返回的函數(shù)對象,并調用 funcg ,此時會發(fā)生 crash 或能看到亂碼。crash 或亂碼是因為,在 funf 中,訪問的 str 對象實際上是 foo() 函數(shù)中定義的棧上臨時對象 stra , foo() 函數(shù)調用結束之后,棧上的臨時對象被釋放, main() 函數(shù)中調用 funcg 實際在訪問一個無效的對象,因而出現(xiàn)問題。

總結

到此這篇關于Lambda表達式里面修改外部變量問題的文章就介紹到這了,更多相關C++ lambda 表達式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • QT+OpenGL實現(xiàn)簡單圖形的繪制

    QT+OpenGL實現(xiàn)簡單圖形的繪制

    這篇文章主要為大家詳細介紹了如何利用QT和OpenGL實現(xiàn)簡單圖形的繪制,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的可以參考一下
    2022-12-12
  • 淺析C語言調試器GDB和LLDB的使用方法

    淺析C語言調試器GDB和LLDB的使用方法

    這篇文章主要介紹了C語言調試器GDB和LLDB的使用方法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12
  • 判斷機器大小端的兩種實現(xiàn)方法

    判斷機器大小端的兩種實現(xiàn)方法

    第一種方法,思路:利用指針的強制類型轉換。第二種方法,思路:利用共用體所有數(shù)據(jù)都從同一地址開始存儲。
    2013-03-03
  • 詳解C++編程中類的成員變量和成員函數(shù)的相關知識

    詳解C++編程中類的成員變量和成員函數(shù)的相關知識

    這篇文章主要介紹了C++編程中類的成員變量和成員函數(shù)的相關知識,是C++入門學習中的基礎知識,需要的朋友可以參考下
    2015-09-09
  • VC6.0常見編譯錯誤提示附解決方法

    VC6.0常見編譯錯誤提示附解決方法

    這篇文章主要介紹了VC++6.0編譯過程中常遇到的一些錯誤提示并給出了錯誤原因與分析,需要的朋友尅參考下
    2013-07-07
  • C語言實現(xiàn)貪吃蛇超詳細教程

    C語言實現(xiàn)貪吃蛇超詳細教程

    本文詳細講解了C語言實現(xiàn)貪吃蛇的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-12-12
  • C++深入探究哈希表如何封裝出unordered_set和unordered_map

    C++深入探究哈希表如何封裝出unordered_set和unordered_map

    哈希表是一種根據(jù)關鍵碼去尋找值的數(shù)據(jù)映射結構,該結構通過把關鍵碼映射的位置去尋找存放值的地方,說起來可能感覺有點復雜,我想我舉個例子你就會明白了,最典型的的例子就是字典
    2022-06-06
  • 最大對稱字符串的算法

    最大對稱字符串的算法

    題目:輸入一個字符串,輸出該字符串中對稱的子字符串的最大長度。比如輸入字符串“google”,由于該字符串里最長的對稱子字符串是“goog”,因此輸出4。
    2013-03-03
  • 淺談c++ 預處理器

    淺談c++ 預處理器

    這篇文章主要介紹了c++ 預處理器的的相關資料,文中示例代碼非常詳細,供大家參考和學習,感興趣的朋友可以了解下
    2020-06-06
  • 詳解C++中的ANSI與Unicode和UTF8三種字符編碼基本原理與相互轉換

    詳解C++中的ANSI與Unicode和UTF8三種字符編碼基本原理與相互轉換

    在C++編程中,我們有時需要去處理字符串編碼的相關問題,常見的字符編碼有ANSI窄字節(jié)編碼、Unicode寬字節(jié)編碼及UTF8可變長編碼。很多人在處理字符串編碼問題時都會有疑惑,即便是有多年工作經驗的朋友也可能搞不清楚。所以有必要講一下這三種字符編碼以及如何去使用它們
    2021-11-11

最新評論

旬阳县| 积石山| 九江县| 航空| 门头沟区| 岳阳县| 久治县| 涿鹿县| 育儿| 故城县| 陇西县| 西乡县| 伊川县| 磐石市| 中超| 上杭县| 隆昌县| 阿拉善左旗| 新平| 盐城市| 天全县| 奉贤区| 德保县| 同德县| 日照市| 廉江市| 临桂县| 寻甸| 安义县| 毕节市| 彭水| 关岭| 固始县| 肇州县| 易门县| 布拖县| 漯河市| 略阳县| 博湖县| 庐江县| 和田县|