[모던 C++] auto와 람다식

Jin Hur·2021년 8월 12일
0

C++

목록 보기
7/18

std::auto

auto를 이용한 쉬운 변수 선언

  • auto 키워드는 C++ 11 표준부터 의미가 수정되어, 변수 선언문으로부터 변수의 타입을 추론하여 결정하도록 지시함
    • 컴파일러가 추론을 통해 타입을 지정
  • auto는 복잡한 형식의 변수 선언을 간소하게 해주며, 타입 선언의 오타나 번거로움을 줄일 수 있게함
  • 컴파일러는 R-value를 통해 추론하여 변수의 타입을 결정함

기본 사례

  1. auto pi = 3.14; // pi는 double 타입으로 선언됨.
    컴파일러는 3.14로부터 추론한 결과 pi를 double 타입으로 결정.
    auto n = 3; // 3이 정수이므로 n을 int로 선언

  2. const, 포인터 변수 선언 시의 '*'를 함께 사용할 수도 있다.
    const auto ff = f; // <=> const float ff = f;
    auto ptr = &n; // <=> int* ptr = &n;
    auto* p = &n; // <=> int* p = &n;
    참고로 포인터 변수의 값으로 지정할 수 없는 것은 에러가 발생한다.
    auto* p = n; // n은 정수형 변수, 에러 발생

  3. 참조 변수의 선언에도 auto를 사용할 수 있다.
    int n = 10;
    int & ref = n; // ref는 int에 대한 참조 변수로 선언
    auto ref2 = ref; // ref2는 int& 타입의 변수로 자동 선언

  4. 함수의 리턴 타입으로부터 추론하여 변수 타입 선언
    int square(int x){ return x*x; }
    auto ret = square(3); // 변수 ret은 int 타입으로 자동 선언
    컴파일러는 square()의 리턴 타입이 int이므로 변수 ret의 타입을 int로 추론. square() 함수가 double을 리턴하는 함수로 수정되면 변수 ret의 타입도 double 타입으로 자동 결정됨.
    이는 auto 키워드를 통해 수정에 따른 오류 가능성을 낮추는 효과가 있을 수 있음을 보여준다.

  5. STL 템플릿에 활용
    vector<int> v = {1, 2, 3, 4, 5};
    iterator을 이용하여 벡터 v의 원소를 출력하려면 보통 다음 코드를 작성
    for(auto it = v.begion(); it != v.end(); it++)
    cout << *it << endl;
    => auto 키워드를 이용하여 간편화, 변수 it는 vector<int>::iterator 타입으로 추론됨.

주의할 점

기본 auto는 const와 &를 무시함

int num = 10;
int& ref = num;
const int con = num;

auto a1 = ref;		// <=> int a1 = ref;
auto a2 = con;		// <=> int a2 = con;

// auto로 참조 타입을 만들고 싶으면,
auto& a3 = ref;
// 또는 
auto a4 = static_cast<int&>(ref);

언제 사용하면 좋을까?

  • 가독성을 위해 일반적으론 자료형을 명시
  • map, 요소 자료형이 복잡한 vector 또는 반복자 사용 시 코드가 길어질 때 종종 사용하자

람다 대수와 람다식

C++11 부터 도입된 람다는 람다 대수(lambda calculis)에서 유래. 람다 대수에서 람다식(lambda expression)은 수학의 함수를 단순하게 표현하는 방법.

수학의 함수: f(x, y) = x + y
람다식: (x, y) -> x + y

위 식처럼 수학에서 이름없는 함수를 람다식이라고 부른다. 다음과 같이 괄호와 함께 x, y에 대입될 값을 지정하면 람다식의 계산이 이루어짐.

((x, y) -> x + y)(2, 3)
= 2 + 3
= 5

C++은 C++11 표준부터 이러한 람다식을 지원한다.

C++ 람다식 선언

프로그래밍의 세계에서 람다는 이름 없느 익명 함수(anonymous function)으로, 람다식 혹은 람다 함수로 불림.

[](int x, int y){ cout << x + y; };		// 매개변수 x, y의 합을 출력하는 람다 작성
[](int x, int y) -> int { return x + y; };	// 매개변수 x, y 합을 리턴하는 람다 작성
[](int x, int y){ cout << x + y; }(2, 3);	// x에 2, y에 3을 대입하여 코드 실행, 5 출력
  • 캡쳐 리스트: 람다식의 외부에 선언된 변수(지역변수, 전역 변수) 목록으로, 람다식에서 사용하고자 할 때 나열하며 다음과 같은 여러 표현 존재.
표현		의미				
[x]		변수 x의 값 활용		
[=]		모든 변수의 값 활용
[&x]		참조 변수 x 활용
[&]		모든 참조 변수 활용
  • 매개변수 리스트: 보통 함수의 매개변수 리스트와 같음
  • 함수 바디: 람다식이 호출될 때 실행되는 코드, 함수를 작성하는 방법과 동일.

auto로 람다식 저장 및 호출

람다식의 형식은 컴파일러에만 알려져 있기에 개발자가 람다식을 저장하는 변수를 직접 선언할 수 없음. 개발자가 람다식을 저장하는 변수를 직접 선언할 수 없음. auto를 이용하면 람다식을 저장하는 변수를 쉽게 선언할 수 있음.

// auto로 람다식 다루기
#include <iostream>
#include <string>
using namespace std;

int main(){
	auto love = [](strint a, string b){
    		cout << a << "보다  " << b << "가 좋아" << endl;
       	};
        
	love("돈", "너");	// 람다식 호출
    love("냉면", "만두");	// "" 
    

캡처 리스트와 리턴 타입을 가지는 람다식 만들기

람다식은 캡처 리스트를 이용하여 주변의 non-static 변수들(지역 변수나 멤버 변수)에 대해 값을 복사하여 받거나 참조를 활용할 수 있음. 아래 예제는 람다식이 캡처 리스트를 통해 외부 변수의 값을 전달받는 사례이다.

#include <iostream>
using namespace std;

int main(){
	double pi = 3.14;	// 지역 변수
    
    auto calc = [pi](int r) -> double { return pi*r*r; };
    
    cout << "면적은 " << calc(3);
}

다음 예제는 캡처 리스트를 통해 지역변수의 참조를 전달받고, 참조를 이용하여 지역변수를 변경하는 사례

#include <iostream>
using namespace std;

int main(){
	int sum = 0;	// 지역변수
    
    [&sum](int x, int y){ sum = x + y; } (2, 3);	// 합 5를 지역변수 sum에 저장.
    
    cout << "합은 " << sum;
}

STL 템플릿에 람다식 활용

람다식은 STL 템플릿을 사용하여 프로그램을 작성할 때 더욱 유용. 아래 예제는 람다식 없이 STL 알고리즘을 활용하는 경우이다.

#include <iostream>
#include <vector>
#include <algorithm>	// for_each() 함수
using namespace std;

void print(int n){
	cout << n << " ";
}

int main(){
	vector<int> v = {1, 2, 3, 4, 5};
    
    for_each(v.begin(), v.end(), print);
}

람다식을 적용하여 전체 코드를 깔끔하게(print 함수는 for_each 함수에서만 사용됨)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
	vector<int> v = {1, 2, 3, 4, 5};
    
    for_each(v.begin(), v.end(), [](int n){ cout << n << " "; });
}

C++에서 람다식의 의미

C++ 프로그램 작성 시 람다가 꼭 필요한 기능은 아님. 다만 간단하고 짧게 최적화된 코드를 작성하는데 도움이 됨. 특별히 람다가 유용한 경우는 다음과 같음.

장점: 간단한 함수를 빠르게 만들어 구현할 수 있습니다.
단점: 디버깅하기 힘들어질 수 있습니다.
(활용)
1. 한 번만 호출하고 재사용하지 않기 때문에 함수에 이름을 붙일 필요가 없는 경우
2. STL 알고리즘 함수의 매개변수에 연산 코드를 넘기는 경우, 연산 코드를 익명의 람다식으로 작성.

0개의 댓글