momodudu.zip

#3 Capturing variables in Lambda 본문

C++

#3 Capturing variables in Lambda

ally10 2020. 11. 25. 11:23

 

c++을 제대로 안쓴지가 넘 오래돼서 개념이 오락가락하기 시작한다;;; lamda에서 변수 캡쳐 과정에 대해서 다시한번 짚어보자..

 

이걸 짚어보기전에, 스위프트랑 달라서 헷갈렸던 호출 방식 부터 훑어보자. call by reference 와 call by value.

 

call by reference는 어떤 변수의 값을 참조, address로 받으면서 기존 variable이 그대로 넘겨진다. 여기서 "그대로"는 카피 없이, 즉 기존의 오브젝트 혹은 변수가 그대로 전달이 된다. 그래서 전달된 함수 scope내에서 이 변수를 수정하면, 수정이 가능하고 함수 스코프 밖으로 빠져나와도 이 변경사항이 반영이 된다.

 

반면 call by value는 말그대로 "value"를 전달한다. 내가 어떤 객체를 선언해서, 어떤 함수로 이 객체를 call by value로 전달한다면 이 값은 그대로 카피가 되고, 함수 scope내에서 파라미터로 전달된 값은 전달되기 전의 오브젝트와는 별개의 오브젝트가 된다. 따라서 함수 스코프 내에서 수정한 반영사항은 함수 밖으로 적용이 되지 않을 것이다.

 

 

람다에서 캡쳐를 한 variable들은 어떻게 전달이 될까!?

 

람다에서는 &, 혹은 = 등의 식을 사용해서 명시적으로 특정 변수들을 call by ref, call by value로 캡쳐할 수 있다. 여기서 유의해야 할 점은!!! 바로 캡쳐한 변수들은 모두 const 라는 점이다. 일반 function에서는 파라미터에 const keyword를 명시적으로 써줘야 해당 파라미터가 const 처리가 되지만, 람다에서는 별도의 const keyword없이도 모든 파라미터가 const다.

 

std::string param1 = "hello world";
auto testFunc = [param1] {
    param1 = "goodbye"; // compile error
};

위 예제는 간단하게, 람다 밖에서 선언한 string을 캡쳐한 후 람다 내에서 이 값을 변경해보려고 하는 예제다. string을 call by value로 캡쳐했을 경우, 그 값은 단순 카피가 되어 전달될뿐만 아니라, 묵시적으로 const keyword가 붙음으로써 람다 내에서 이 값을 임의로 변경하는것은 불가능하다. 이 경우에는 일단 컴파일 에러가 뜬다.

 

 

그렇다면 call by ref는 어떨까? call by ref의 const는 참조의 개념이 제대로 잡혀있지 않다면 사실 혼동의 여지가 있다. 

std::string param1 = "hello world";
auto testFunc = [&param1] {
    param1 = "goodbye"; // works
};

이 경우에는 컴파일에러가 나지도 않고, 정상적으로 값도 변경된다. const라매? 어떻게 이럴수있나? 싶겠지만, 사실 람다에서 캡쳐한건 param1 자체가 아니라, param1을 담고있는 주소값이라고 보면 된다. 그래서 변경이 불가능한건 param1의 주소값이며, 이 주소값 access를 통해서 param1에서는 변경이 가능하다는 뜻!!!!

 

 

사실.. 이번 포스팅을 쓰게 된 계기는, 람다에서 아래와 같은 사용이 가능할 줄 알았다.

 

struct MyStruct {
    int a;
    int b;
};

void testFunc(MyStrcut var)
{
    var.a = 20;
    std::cout << var.a ;
}

void main() 
{
    MyStruct test;
    test.a = 10;
    
    testFunc(test);
}

 

일반 함수에서는 파라미터를 call by value로 전달하더라도, 함수 내부 local variable 스택에 카피된 값이 쌓이고, 이 값을 변경하는것이 가능하다. 물론 함수 밖으로 빠져나가면 변경한 값은 의미가 없어지지만.. 단순히 함수 내부에서는 이 카피한 값을 read뿐만 아니라 write도 가능하다. 별도로 const를 붙이지 않는 이상!

 

그래서 람다에서도 이게 가능할 줄 알았다.

 

struct MyStruct {
    int a;
    int b;
};

void main () 
{
	
    MyStruct test;
    test.a = 10;
	auto testFunc = [test] {
    	test.a = 20; 	// compile error!!!
        std::cout << test;
    }
}

 

이렇게.. 람다는 이름없는 함수일뿐이니까, call by value로 전달해서 임시로 카피된 객체를 변경할 수 있을줄 알았는데 사실 캡쳐된 test는 자동으로 const keyword가 붙으면서 변경이 불가능하므로, 애초에 컴파일 에러가 뜬다..하핳... 글쿤 그래서 사실 위 람다식은 아래의 function처럼, 파라미터에 const가 붙는 이름없는 함수!!! 라고 정의할 수 있겠다.

 

struct MyStruct {
    int a;
    int b;
};

void testFunc(const MyStrcut var)
{
    var.a = 20; // compile error!!
    std::cout << var.a ;
}

void main() 
{
    MyStruct test;
    test.a = 10;
    
    testFunc(test);
}

 

 

그래서 람다안에서는 call by value로 캡쳐한건 변경할 수 없다 ^_^

아, 그렇지만 명시적으로 람다에 mutable 키워드를 붙여주면 람다 안에서도 값 변경이 가능하다고 합니당.

'C++' 카테고리의 다른 글

Generics - template instantiation, specialization  (0) 2022.11.22
#5 constexpr keyword  (0) 2022.08.01
#4 std::uniform_int_distribution  (0) 2022.08.01
#2 C++ const keyword와 오버로딩에 관하여  (0) 2019.10.01
#1 C++ STL Container  (0) 2019.08.28