[미리 보는 C++11] 4. constexpr - 2

C++0x 2011.09.23 09:30 Posted by 흥배

constexpr를 클래스에 사용


constexpr을 클래스에서 사용하면 클래스를 정수로 사용할 수도 있으며 메타 템플릿 프로그래밍에서는 이전에는 복잡하게 처리하던 것을 아주 간단하게 처리할 수도 있습니다. C++ 메타 템플릿 프로그래밍에 관심이 많구나 자주 사용하고 있는 분들에게는 constexpr 덕분에 프로그래밍이 한결 편해지리라 생각합니다.

 

아래의 코드는 Integer 이라는 클래스를 constexpr을 사용하여 정수처럼 사용 합니다.

class Integer

{

private :

    int value ;

 

public :

    constexpr Integer() : value() { }

    constexpr Integer( int value ) : value(value) { }

 

    constexpr operator int() { return value ; }

} ;

 

int main()

{

    constexpr Integer size = 5 ; // 컴파일 타임에 정수로

 

    int x[size] ; // Integer::operator int()가 호출된다

 

    Integer object ; // 일반적인 클래스 인스턴스 화. 실행 시에 처리

    int y[object] ; // 당근 에러

}

출처 : http://cpplover.blogspot.com/2010/11/gccniconstexpr.html

 


또 메타 템플릿 프로그래밍에서는 아래와 같이 사용할 수도 있습니다.

#include <iostream>

 

struct pi {

    static constexpr double value = 3.14;

};

 

template <const double& r>

struct circle_area {

    static constexpr double value = r * r * pi::value;

};

 

struct radius {

    static constexpr double value = 2.5;

};

 

int main()

{

    constexpr double result = circle_area<radius::value>::value;

 

    static_assert(result == 19.625, "not equal");

    std::cout << result << std::endl;

}

출처 : http://d.hatena.ne.jp/faith_and_brave/searchdiary?word=constexpr&.submit=%B8%A1%BA%F7&type=detail

 

 

constexpr은 컴파일 할 때 결과가 이미 결정 나는 것은 컴파일 타임 때 처리를 해주어 실행 시에 불필요한 처리를 막아주고, 기존의 메타 템플릿 프로그래밍으로 까다롭게 만들었던 것을 아주 쉽게 구현할 수 있게 해줍니다.

 

C++11에서는 constexpr을 잘 사용하면 기존 보다 더 뛰어난 프로그래밍을 할 수 있으니 깊게 파고들 가치가 있다고 생각합니다.

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[미리 보는 C++11] 3. constexpr - 1

C++0x 2011.09.15 09:00 Posted by 흥배

constexpr는 변수, 함수, 클래스를 컴파일 타임에 정수로 사용할 수 있도록 해줍니다. 즉 상수로 취급할 수 있는 작업은 컴파일 타임에 처리하도록 할 수 있습니다.

 

constexpr를 변수에 사용

constexpr int aa = 11;

이것은

const int aa = 11

와 같은 의미를 가집니다.

 

그러나 아래와 같이는 사용할 수 없습니다.

int input_num = 0;

constexpr int aa = input_num;  // 에러


constexpr
로 지정된 변수는 꼭 컴파일 시에 정수가 되기 때문에 변수 선언 시 대입이 정수 식이어야만 합니다. const와의 차이는 const는 컴파일 시에 정수가 아니어도 괜찮고 변수 선언 시 대입 값이 정수 식인 경우 정수 식이 되고, 그렇지 않은 경우는 단순히 const를 수식하는 것이 됩니다(이에 비해 constexpr는 꼭 정수 식이어야만 합니다).

 

 

 

constexpr를 함수에 사용

C++03에서는 아래의 코드는 에러가 됩니다.

int GetNum() { retun 5; }

int Numbers[ GetNum() ];

GetNum 함수는 상수 5를 반환 하는 것으로 이미 컴파일 시에 반환 값을 알 수 있습니다. 그러나 컴파일러는 GetNum 이라는 함수가 정수처럼 사용할 수 있는지 알 수 없으므로 정수로 취급하지 않습니다.

 

위 코드는 C++11constexpr를 사용하면 우리가 원하는 대로 GetNum 함수를 정수로 사용할 수 있습니다.

 

constexpr int GetNum() { retun 5; }

int Numbers[ GetNum() ];

 

constexpr를 함수에 사용할 때는 꼭 함수 본체는 { return expression; } 형태가 되어야만 합니다.

 

 

constexpr 변수는 비 constexpr 변수에 사용할 수 있으므로 아래와 같은 테크닉도 사용할 수 있다.

constexpr double power( double x, unsigned int y )

{

    return y == 1 ? x : x * power( x, y - 1 ) ;

}

 

int main()

{

    // 정수 식

    constexpr double a = power( 2, 32 ) ;

 

    // 정수 식이 아니다

    double x = 2 ; unsigned int y = 32 ;

    double b = power( x, y ) ;

}

(출처) http://cpplover.blogspot.com/2010/11/gccniconstexpr.html

 

 

그리고

const int base_HP = 200;

int NPC_Lv1_HP = base_HP + 0;

int NPC_Lv2_HP = base_HP + 200;

라는 코드는 정수 계산을 하는데 실행 시에 계산되는데 이것을 constexpr을 사용하여 컴파일 시에 계산되게 할 수 있습니다.

 

constexpr int AssignHP( int nPlusHP )

{

 return base_HP + nPlusHP;

}

 

int NPC_Lv1_HP = AssignHP( 0 );

int NPC_Lv2_HP = AssignHP( 200 );



 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
앞으로 조금씩이라도 꾸준히 C++11에 새로 추가되는 기능들을 간단하게 소개하려고 합니다.
새로운 라이브러리의 경우 boost 라이브러리에 있는 것은 boost 라이브러리를 통해서 예제와 같이
좀더 자세하게 설명하고 그렇지 못한 것들은 간단한 설명과 코드로만 설명하려고 합니다.

C++ 프로그래머에게 새로운 C++ 표준은 먼 미래의 것이 아닙니다. 지금부터 조금씩 공부해보죠^^
(개인적으로 예전에 STL의 경우를 보면 앞으로 C++11을 아는 C++ 프로그래머와 모르는 프로그래머로 나누어지지 않을까 생각합니다)



현재의 C++에서는 두 가지 종류의 문자형을 지원하고 있습니다. char wchar_t 입니다.

char szName[] = “jacking”;

wchar_t szName2[] = L”jacking”;

 

그러나 C++0x에서는 유니코드를 강력하게 지원하기 위해 새로운 문자형이 추가 됩니다.

 

 

 

UTF-8

UTF-8을 사용하는 문자형은 따로 없고 기존의 char를 사용합니다.

char szName[] = u8”jacking”;

문자열 리터럴(literal) 앞에 u8을 붙입니다.

 

 

UTF-16

UTF-16을 사용하는 문자형 변수를 선언할 때는 char_16t를 사용합니다.

char16_t szName3[] = u”jacking”;

문자열 리터럴 앞에 u를 붙입니다.

 

 

UTF-32

UTF-32을 사용하는 문자형 변수를 선언할 때는 char_32t를 사용합니다.

char32_t szName4[] = U”jacking”;

문자열 리터럴 앞에 U를 붙입니다.



저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

C++0x가 드디어 C++11이 되었습니다.

C++0x 2011.08.15 16:02 Posted by 흥배

새로운 C++ 표준 작업이 언제쯤에나 끝날지 고대하고 있는 분들에게 반가운 소식이 있습니다.

드디어 C++0x 작업이 거의 마무리 되었습니다.

 

저번 주 수요일에 최종 국제 투표가 끝난 후 드디어 결과가 나왔는데 만장 일치로 승인이 되었습니다. 이제는 앞으로 몇 달 후에 ISO로부터 최종 발행만 기다리면 됩니다(즉 서류적인 절차만 남았습니다).

 

이로써 길게 길게 진행된 C++의 새로운 표준 작업은 끝나게 되어서 이제 C++11로 불리게 되었습니다( 이전에는 C++98, C++03이 있었습니다 ).

 

C++은 특정 회사가 주도하지 않고, 기존에 C++로 만들었던 코드가 문제 없이 동작해야 하고(표준 사양만 지켰다면), 성능과 편리성을 모두 가지려고 하니 많은 시간이 걸렸습니다.

 

아직 ISO에서 최종 문서가 나오지 않았고 무료로 최종 사양이 어떻게 되었는지 궁금한 분들은 http://t.co/5mjCzyJ 를 통해서 문서를 받아보기 바랍니다. 이 문서는 워킹 드래프트 3242 ISO에서 나올 사양 문서와 거의 같을 것입니다(참고로 ISO에서 나온 문서는 유료입니다).

 

이제 우리는 앞으로 나올 Visual C++이 과연 얼만큼 새로운 표준 기능을 구현해 줄지가 기대됩니다. 개인적으로 꽤 많은 부분이 구현되리라 생각하고 혹 빠진 부분은 예전의 tr1처럼 서비스 팩에서 구현되지 않을까 생각합니다.

 

C++ 새로운 표준이 정해졌으니 남보다 앞서기를 바라는 C++ 프로그래머들은 새로운 C++ 표준을 공부해 봅시다. ^^

 

근래에 바빠서 글을 거의 올리지 못했는데 다음 글은 새로운 C++ 표준의 바뀐 부분을 간단하게 설명하는 글을 올릴 예정입니다.

그리고 새로 추가되는 표준 라이브러리는 boost 라이브러리에서 많이 들어왔습니다. 그래서 지금이라도 boost 라이브러리를 다운로드 받으면 새로운 표준에 들어갈 라이브러리를 미리 사용할 수 있습니다.

 

 


참고

C++0x 사양

위키피디아

http://en.wikipedia.org/wiki/C%2B%2B0x

GCC feature 테이블

http://gcc.gnu.org/projects/cxx0x.html

 

boost 라이브러리

설치 버전 다운로드

http://www.boostpro.com/download/

공식 홈페이지

http://www.boost.org

 

 

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

STL의 컨테이너를 사용해보았다면 forward_list라고 해서 딱히 어려운 부분은 없습니다. 다만 forward_list이 단 방향 리스트라는 것과 다른 컨테이너에서는 지원하는 기능이 일부 없다는 것을 잘 숙지해야 합니다.

 

필요한 헤더 파일

forward_list는 이름과 같은 ‘forward_list’라는 헤더 파일을 포함해야 합니다.

#include <forward_list>

 

 

[예제] forward_list를 사용하여 요소 추가, 순회, 삭제하기

#include "stdafx.h"

#include <iostream>

#include <forward_list>

 

using namespace std;

 

 

int main()

{

           forward_list< int > flist;

 

 

           cout << "flist에 추가한 요소들 출력" << endl;

           // 추가하기

           auto iter = flist.before_begin();

           for( int i = 0; i < 5; ++i )

           {

                     iter = flist.insert_after( iter, i );

           }

                    

           // 순회

           for( iter = flist.begin(); iter != flist.end(); ++iter )

           {

                     cout << *iter << endl;

           }

 

           cout << endl;

           cout << "flist의 요소들 중 일부를 삭제한 후 남은 요소들 출력" << endl;

           // 순회 하면서 일부 요소 삭제

           auto prev_iter = flist.before_begin();

           iter = flist.begin();

           while( iter != flist.end() )

           {

                     if( 3 == *iter )

                     {

                                iter = flist.erase_after( prev_iter );

                                continue;

                     }

                     ++prev_iter;

                     ++iter;

           }

 

           // 순회

           for( iter = flist.begin(); iter != flist.end(); ++iter )

           {

                     cout << *iter << endl;

           }

 

           return 0;

}

 

< 결과 >


 

위 예제를 보면 아시겠지만 forward_list std::list에 비해 성능 면의 이점을 가지고 있지만 사용 측면에서는 조금 불편한 점이 좀 있습니다. 그러나 C와 비슷한 성능을 내고 싶은 경우에는 좋은 선택 기가 될 수도 있습니다.

 


참고

http://msdn.microsoft.com/ko-kr/library/ee373568.aspx

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

앞에까지는 STL의 알고리즘에 추가된 것들을 다루었는데 이번에는 컨테이너 하나를 소개하겠습니다. 사실 이 컨테이너는 저도 얼마 전까지만 하더라도 새로 추가 된지 몰랐습니다.^^;

 

새로 추가된 컨테이너의 이름은 forward_list입니다.

이름을 들어보니 대충 어떤 컨테이너인지 감이 오시죠?^^ 네 이 컨테이너는 기존의 list 컨테이너와 비슷한 종류의 컨테이너입니다.

 

 

forward_list를 만든 이유

표준 라이브러리(STL)에는 이미 리스트(std::list) 라이브러리가 있습니다. 이것은 쌍 방향 리스트입니다. list는 사용하기는 편하지만 사용 메모리나 처리 속도에 조금 아쉬운 점이 있습니다. 또 대 부분의 상황에서 쌍 방향 리스트가 필요한 경우보다는 단 방향 리스트만으로 충분한 경우가 자주 있습니다. 이런 이유로 C++0x에서는 단 방향 리스트를 추가하기로 했습니다.

 

 

forward_list의 설계 방침

1. 특별한 이유가 없다면 forward_list는 기존의 list의 설계에 맞춘다.

2. 설계 상의 선택 기가 여러 개인 경우 성능(속도와 사이즈)을 최우선 한다(C의 구조체로 구현하는 경우와 비교하여 Zero Overhead로 한다).

3. std::list insert eraseforward_list에서도 제공할 수 있지만 구현이 복잡해지고 성능 측면에서 좋지 않으므로 제공하지 않는다.

4. 다른 STL의 컨테이너들에 있는 size 함수를 제공하지 않는다. 이유는 요소 수를 보존하는 멤버를가지고 있으면 C언어에서 구현한 것과 비교해서 불필요한 메모리를 사용한다. 만약 이런 멤버를 가지고 있지 않으면서 size 함수를 지원하면 호출할 때마다 모든 요소를 세어야 하므로 계산량이 O(N)이 된다(그런데 유저는 다른 컨테이너와 같이 size의 계산량이 작을 것이라고 생각할 수 있다). 또 이미 unordered와 같은 연상 컨테이너도 기존의 요소를 만족하지 않고 있다.

 

 

STL list 컨테이너와 다른 점

forward_list는 기존의 list와 아래와 같은 점이 다릅니다.

1. forward_list는 단 방향 리스트(singly-linked-list)이다. 각 요소는 그 다음 요소를 가리키는 포인터를 하나만 가지고 있다(list은 양 방향 리스트).

2. (단 방향 리스트이므로) list에 비해서 메모리를 작게 사용한다. 이것은 각 요소의 메모리만이 아닌 컨테이너 그 자체의 사이즈도 작다. int 형에 대해서 list 12바이트라면 forward_list 8바이트이다(64비트에서는 각각 24, 16).

3. list에 비해 삽입/삭제 속도가 더 빠르지만 그 차이는 크지는 않다

4. 한 방향으로만 이동할 수 있다.

5. 삽입과 삭제는 지정한 요소의 다음 요소만 가능하다.

 

 

forward_list의 멤버 리스트

기능

멤버

대입

assign

반복자

befor_begin

 

cbefore_begin

 

begin

 

end

 

cbegin

 

cend

비었는지 조사

empty

현재 크기(size)

지원 안함

사이즈 변경

resize

모두 삭제

clear

선두에 추가

push_front

선두 요소 삭제

pop_front

선두 요소 참조

front

삽입

insert_after

삭제

erase_after

조건 삭제

remove

 

remove_if

중복 요소 삭제

unique

교환

swap

병합

merge

정렬

sort

반전

reverse

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

C++03까지의 STL에는 데이터셋에서 가장 작은 요소를 찾을 때는 min_element, 가장 큰 요소를 찾을 때는 max_element를 사용하였습니다.

그런데 만약 최소와 최대를 동시에 찾을 때는 어쩔 수 없이 min_element max_element를 각각 호출해야 하는 불필요한 불편한 점이 있었습니다.

 

C++0x에서는 이런 불편함을 개선하기 위해 한번에 최소와 최고를 찾아주는 minmax_element 알고리즘이 새로 생겼습니다.

 

 

minmax_element

template<class ForwardIterator>

    pair< ForwardIterator, ForwardIterator >

        minmax_element( ForwardIterator _First, ForwardIterator _Last );

template<class ForwardIterator, class BinaryPredicate>

    pair< ForwardIterator, ForwardIterator >

        minmax_element( ForwardIterator _First, ForwardIterator _Last, BinaryPredicate _Comp );

 

minmax_element 알고리즘에는 조건자를 사용하는 버전과 조건자를 사용하지 않은 버전 두 가지가 있습니다. 데이터셋의 자료형이 유저 정의형(class struct를 사용한)이라면 조건자가 있는 버전을 사용합니다.

 

< 예제 코드 >

#include <iostream>

#include <algorithm>

using namespace std;

 

 

int main()

{

           int Numbers[10] = { 50, 25, 20, 7, 15, 7, 10, 2, 1, 3 };

          

           pair<int*, int*> MinMaxValue = minmax_element( &Numbers[0], &Numbers[10] );

 

           cout << "최소 값 : " << *MinMaxValue.first << endl;

           cout << "최대 값 : " << *MinMaxValue.second << endl;

          

           return 0;

}

 

< 결과 >


 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

데이터셋을 시퀸스(연속적인)한 값으로 채우고 싶을 때는 iota 알고리즘을 사용합니다.

앞서 소개한 알고리즘들은 <algorithm> 헤더 파일에 정의 되어 있는 것에 반해 iota 알고리즘은 <numeric> 헤더 파일에 정의 되어 있습니다.

 

itoa

template<class ForwardIterator, class T>

  void iota(ForwardIterator first, ForwardIterator last, T value);

 

 

아래는 예제 코드와 결과 입니다.

#include <iostream>

#include <vector>

#include <numeric>

using namespace std;

 

int main()

{

           vector<int> Numberlist;

           Numberlist.push_back( 2 );

           Numberlist.push_back( 5 );

           Numberlist.push_back( 7 );

           iota( Numberlist.begin(), Numberlist.end(), 2 );

 

           for( auto IterPos = Numberlist.begin(); IterPos != Numberlist.end(); ++IterPos )

           {

                     cout << *IterPos << endl;

           }

 

           return 0;

}

 

< 결과 >

 

위 예제를 보면 아시겠지만 iota의 세 번째 인자의 값이 시작 값이고, 이후에 값이 하나씩 증가합니다.

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

is_heap is_heap_until는 앞서 소개했던 is_sorted, is_sorted_until과 비슷한 알고리즘입니다. 차이가 있다면 is_heap is_heap_until는 정렬이 아닌 Heap을 다룬다는 것만 다릅니다.

 

is_heap은 데이터셋이 Heap으로 되어 있는지 아닌지, is_heap_until는 데이터셋에서 Heap이 아닌 요소의 첫 번째 위치를 반환합니다.

 

is_heap

template<class RandomAccessIterator>

    bool is_heap(

        RandomAccessIterator _First,

        RandomAccessIterator _Last

    );

template<class RandomAccessIterator, class BinaryPredicate>

    bool is_heap(

        RandomAccessIterator _First,

        RandomAccessIterator _Last,

        BinaryPredicate _Comp

    ); 

 

 

is_heap_until

template<class RandomAccessIterator>

    bool is_heap_until(

        RandomAccessIterator _First,

        RandomAccessIterator _Last

);

template<class RandomAccessIterator, class BinaryPredicate>

    bool is_heap_until(

        RandomAccessIterator _First,

        RandomAccessIterator _Last,

        BinaryPredicate _Comp

);

 

 

is_heap is_heap_until는 각각 조건자를 사용하는 버전과 사용하지 않는 버전 두 개가 있습니다. 조건자를 사용하지 않는 경우는 operator< 를 사용합니다.

 

 

그럼 is_heap is_heap_until을 사용한 아주 간단한 예제 코드를 봐 주세요^^

#include <iostream>

#include <algorithm>

using namespace std;

 

 

int main()

{

           int Numbers1[10] = { 50, 25, 20, 7, 15, 7, 10, 2, 1, 3 };

           int Numbers2[10] = { 50, 25, 20, 7, 15, 7, 10, 6, 11, 3 };

           int Numbers3[10] = { 50, 25, 20, 7, 15, 16, 12, 3, 6, 11 };

          

          

           bool IsResult = false;

           IsResult = is_heap( &Numbers1[0], &Numbers1[10], [](int x, int y) { return x < y; } );

           cout << "Numbers1 Heap인가 ? " << IsResult << endl;

 

           IsResult = is_heap( &Numbers2[0], &Numbers2[10], [](int x, int y) { return x < y; } );

           cout << "Numbers2 Heap인가 ? " << IsResult << endl;

 

           IsResult = is_heap( &Numbers3[0], &Numbers3[10] );

           cout << "Numbers3 Heap인가 ? " << IsResult << endl;

 

           cout << endl;

           int* NumIter = is_heap_until( &Numbers2[0], &Numbers2[10], [](int x, int y) { return x < y; } );

           cout << "Numbers2에서 Heap되지 않은 첫 번째 위치의 값 : " << *NumIter << endl;

 

           return 0;

}

 

< 결과 >

 

 

 

ps : 자료구조 Heap에 대해서 잘 모르시는 분들은 아래의 글을 참고해 주세요

http://blog.naver.com/ctpoyou/105423523


저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

is_sorted는 데이터셋이(컨테이너나 배열) 정렬되어 있다면 true를 반환하고, 그렇지 않다면 false를 반환 합니다.

is_sorted_until는 데이터셋에서 정렬되어 있지 않는 요소의 첫 번째 위치를 반환합니다.

 

is_sortedis_sorted_until의 원형은 아래와 같습니다.

is_sorted

template<class ForwardIterator>

    bool is_sorted( ForwardIterator _First, ForwardIterator _Last );


template<class ForwardIterator, class BinaryPredicate>

    bool is_sorted( ForwardIterator _First, ForwardIterator _Last, BinaryPredicate _Comp );

 

 

is_sorted_until

template<class ForwardIterator>

    ForwardIterator is_sorted_until( ForwardIterator _First, ForwardIterator _Last);

 

template<class ForwardIterator, class BinaryPredicate>

    ForwardIterator is_sorted_until( ForwardIterator _First, ForwardIterator _Last,

               BinaryPredicate _Comp );

 

위의 is_sortedis_sorted_until의 원형을 보시면 알겠지만 조건자(함수객체)를 사용하는 버전과 사용하지 않는 버전 두 가지가 있습니다.

조건자를 사용하지 않는 경우 기본으로 operator<가 적용됩니다.

 

프로그래머는 코드로 이해하죠? ^^ 그럼 바로 예제 코드 들어갑니다.

이번 예제는 간단하게 만들기 위해 정수 배열을 사용해 보았습니다. 아마 STL을 이제 막 공부하고 있는 분들은 알고리즘을 STL의 컨테이너에만 사용할 수 있는 것으로 알고 있는 분들도 있을텐데 그렇지 않습니다. 아래 예제는 int 형 배열을 사용하였습니다.

 

< 예제 코드 >

#include <iostream>

#include <algorithm>

using namespace std;

 

 

int main()

{

           int Numbers1[5] = { 1, 2, 3, 4, 5 };

           int Numbers2[5] = { 5, 4, 3, 2, 1 };

           int Numbers3[5] = { 1, 2, 4, 3, 5 };

           bool IsResult = false;

 

          

           IsResult = is_sorted( &Numbers1[0], &Numbers1[5], [](int x, int y) { return x < y; } );

           cout << "Numbers1. 오름 차순 ? " << IsResult << endl;

 

           IsResult = is_sorted( &Numbers2[0], &Numbers2[5], [](int x, int y) { return x > y; } );

           cout << "Numbers2. 내림 차순 ? " << IsResult << endl;

 

           IsResult = is_sorted( &Numbers3[0], &Numbers3[5], [](int x, int y) { return x < y; } );

           cout << "Numbers3. 오름 차순 ? " << IsResult << endl;

 

           cout << endl;

           cout << "is_sorted에서 조건자(함수객체)를 생략한 경우 " << IsResult << endl;

           IsResult = is_sorted( &Numbers1[0], &Numbers1[5] );

           cout << "Numbers1 is_sorted의 결과는 ? " << IsResult << endl;

           IsResult = is_sorted( &Numbers2[0], &Numbers2[5] );

           cout << "Numbers2 is_sorted의 결과는 ? " << IsResult << endl;

 

           cout << endl;

           int Numbers4[8] = { 1, 2, 3, 5, 4, 5, 7, 8 };

           int* NumIter = is_sorted_until( &Numbers4[0], &Numbers4[5], [](int x, int y) { return x < y; } );

           cout << "Numbers4에서 정렬되지 않은 첫 번째 위치의 값 : " << *NumIter << endl;

 

           return 0;

}

 

< 결과 >


 

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

이번에 설명할 is_partitioned, partition_point는 그 이름처럼 앞서 소개한 partition_copy와 관계가 있는 알고리즘 입니다.

 

is_partitioned의 원형

template<class InputIterator, class BinaryPredicate>

    bool is_partitioned(

        InputIterator _First,

        InputIterator _Last,

        BinaryPredicate _Comp

    );

 

partition_point의 원형

template<class ForwardIterator, class Predicate>

    ForwardIterator partition_point(

        ForwardIterator _First,

        ForwardIterator _Last,

        Predicate _Comp

    );

 

is_partitioned는 데이터셋의 요소가 전반 부와 후반 부 두 개로 나누어져 있는지 조사할 때 사용하고, partition_point는 두 개로 나누어져 있는 데이터셋에서 후반 부의 첫 번째 요소를 가리키는 반복자를 반환합니다.

 

약간 설명이 애매하죠?^^;

예를 들어 설명하면 온라인 FPS 게임에서 8명이 각각 4명씩 레드 팀과 블루 팀으로 나누어서 게임을 하는 경우 vector로 된 StagePlayers(온라인 게임에서 방에 들어온 유저들을 저장)에 앞 부분에는 레드 팀 플레이어 4명을 차례로 저장하고, 그 이후에 블루 팀 플레이어를 저장하고 있는지 조사하고 싶을 때 is_partitioned 알고리즘을 사용하면 알 수 있습니다(맞다면 true를 반환합니다). 그리고 StagePlayers에서 블루 팀의 첫 번째 플레이어에 접근하고 싶다면 partition_point를 사용합니다.  


나름 쉽게 설명한다고 했는데 이해 가시나요? 만약 이해가 안 간다면 예제 코드를 봐 주세요^^

 

< 예제 >

#include <iostream>

#include <algorithm>

#include <vector>

#include <list>

using namespace std;

 

struct PLAYER

{

           int CharCD;

           bool IsRedTeam;

};

 

 

int main()

{

           vector< PLAYER > StagePlayers1;

           PLAYER player1; player1.CharCD = 1;         player1.IsRedTeam = true;           StagePlayers1.push_back( player1 );

           PLAYER player2; player2.CharCD = 2;         player2.IsRedTeam = true;           StagePlayers1.push_back( player2 );

           PLAYER player3; player3.CharCD = 3;         player3.IsRedTeam = true;           StagePlayers1.push_back( player3 );

           PLAYER player4; player4.CharCD = 4;         player4.IsRedTeam = false;                      StagePlayers1.push_back( player4 );

           PLAYER player5; player5.CharCD = 5;         player5.IsRedTeam = false;                      StagePlayers1.push_back( player5 );

           PLAYER player6; player6.CharCD = 6;         player6.IsRedTeam = false;                      StagePlayers1.push_back( player6 );

           PLAYER player7; player7.CharCD = 7;         player7.IsRedTeam = false;                      StagePlayers1.push_back( player7 );

 

           bool IsPartitioned = is_partitioned( StagePlayers1.begin(), StagePlayers1.end(),

                                                                                     []( PLAYER player ) -> bool { return player.IsRedTeam; } );

           if( IsPartitioned ) {

                     cout << "레드 팀과 블루 팀으로 구분 되어 나누어져 있습니다." << endl;

           } else {

                     cout << "레드 팀과 블루 팀으로 구분 되어 있지 않습니다." << endl;

           }

 

           vector< PLAYER >::iterator IterFirstBlueTeamPlayer = partition_point( StagePlayers1.begin(),StagePlayers1.end(),

                                                     []( PLAYER player ) -> bool { return player.IsRedTeam; } );

           if( IterFirstBlueTeamPlayer != StagePlayers1.end() ) {

                     cout << "첫 번째 블루 팀 플레이어. 캐릭터 코드 : " << (*IterFirstBlueTeamPlayer).CharCD << endl;

           }

 

                    

           vector< PLAYER > StagePlayers2;

           StagePlayers2.push_back( player7 );

           StagePlayers2.push_back( player6 );

           StagePlayers2.push_back( player1 );

           StagePlayers2.push_back( player5 );

           StagePlayers2.push_back( player4 );

           StagePlayers2.push_back( player3 );

           StagePlayers2.push_back( player2 );

          

           IsPartitioned = is_partitioned( StagePlayers2.begin(), StagePlayers2.end(),

                                                                                     []( PLAYER player ) -> bool { return player.IsRedTeam; } );

           if( IsPartitioned ) {

                     cout << "레드 팀과 블루 팀으로 구분 되어 나누어져 있습니다." << endl;

           } else {

                     cout << "레드 팀과 블루 팀으로 구분 되어 있지 않습니다." << endl;

           }

 

           getchar();

           return 0;

}

 

< 결과 >

 

예제 코드를 보니 쉽게 이해 되시죠? 그럼 저는 아는 걸로 생각하고 다음 포스팀에서는 다른 알고리즘을 설명하겠습니다^^

 

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

partition_copy 알고리즘은 하나의 집단에서 서로 다른 두 개의 집단으로 나눌 때 사용하는 것이라고 아주 간단하게 말할 수 있습니다.

 

partition_copy

template<class InputIterator, class OutputIterator1, class OutputIterator2, class Predicate>

    pair<OutputIterator1, OutputIterator2>

        partition_copy(

            InputIterator _First,

            InputIterator _Last,

            OutputIterator1 _Dest1,

            OutputIterator2 _Dest2,

            Predicate _Pred

        );

 데이터셋의 _First _Last 사이에 있는 각 요소 x를 조건자 _Pred에 인자를 넘겼을 때 true를 반환하면 x _Dest1, false를 반환하면 _Dest2에 복사하고 지정된 구간의 모든 요소를 다 처리하면 OutputIterator 값을 pair로 반환한다

 

그럼 좀 더 쉽게 이 알고리즘을 어떤 경우에 사용하는지 알 수 있도록 간단한 예제를 하나 보여드리겠습니다.

 ) 게임 아이템들을 팔 수 있는 것과 팔 수 없는 것으로 나누어라

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

struct ITEM
{
    int nItemCode;
    bool bEnableSell;
};

int main()
{
    vector< ITEM > AllItems;
    ITEM item1; item1.nItemCode = 1;    item1.bEnableSell = false;        AllItems.push_back( item1 );
    ITEM item2; item2.nItemCode = 2;    item2.bEnableSell = true;        AllItems.push_back( item2 );
    ITEM item3; item3.nItemCode = 3;    item3.bEnableSell = true;        AllItems.push_back( item3 );
    ITEM item4; item4.nItemCode = 4;    item4.bEnableSell = false;        AllItems.push_back( item4 );
    ITEM item5; item5.nItemCode = 5;    item5.bEnableSell = true;        AllItems.push_back( item5 );
    ITEM item6; item6.nItemCode = 6;    item6.bEnableSell = false;        AllItems.push_back( item6 );
    ITEM item7; item7.nItemCode = 7;    item7.bEnableSell = true;        AllItems.push_back( item7 );

    ITEM UnItem; UnItem.nItemCode = 0;
    list< ITEM > SellItems( 7, UnItem );            
    list< ITEM > UnSellItems( 7, UnItem );
    
    pair<list< ITEM >::iterator, list< ITEM >::iterator > SeperateItems;
    SeperateItems = partition_copy( AllItems.begin(), AllItems.end(),
                                                 SellItems.begin(),
                                                 UnSellItems.begin(),
                                                   []( ITEM& item ) { return item.bEnableSell; } );

    cout << "팔 수 있는 아이템" << endl;
    for each( ITEM item in SellItems )
    {
        if( item.nItemCode <= 0 ) {
            continue;
        }
        cout << "아이템 코드 : " << item.nItemCode << endl;
    }

    cout << endl << endl;

    cout << "팔 수 없는 아이템" << endl;
    for( auto Iter = UnSellItems.begin(); Iter != UnSellItems.end(); ++Iter )
    {
        if( Iter->nItemCode <= 0 ) {
            continue;
        }
        cout << "아이템 코드 : " << Iter->nItemCode << endl;
    }
    
    getchar();
    return 0;
}

< 결과 >


partition_copy를 사용할 때 한 가지 주의할 점은 결과를 다른 컨테이너에 복사를 하므로 해당 컨테이너에 공간이

확보되어 있어 있어야 합니다. 그래서 위의 예제에도

ITEM UnItem; UnItem.nItemCode = 0;
list< ITEM > SellItems( 7, UnItem );            
list< ITEM > UnSellItems( 7, UnItem );

로 더미 값을 넣어서 복사할 공간을 확보하고 있습니다.

 

참조 : http://msdn.microsoft.com/ko-kr/library/ee384416.aspx

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

트위터에서 @All2one님을 통해서 GCC 컴파일러에서 RValue Reference Move Semantics를 사용했을 경우와 그렇지 않은 경우 STL에서 얼마만큼의 성능 차이가 나는지 테스트를 한 결과를 보았습니다.

사이트 주소는 http://cpp-next.com/archive/2010/10/howards-stl-move-semantics-benchmark/

입니다.

 

이것은 GCC 컴파일러를 사용한 경우라서 저는 VC++을 사용하여 어떤 결과가 나오는지 궁금해서 테스트 해 보았습니다.

RValue Reference을 사용하지 않는 가장 최신의 컴파일러는 VC++ 9(VS2008)이므로 VC++10 VC++9를 같은 코드로 컴파일한 후 실행하였습니다.

 

먼저 테스트한 컴퓨터의 하드웨어 사양은 아래와 같습니다.


 

테스트 코드는 아래와 같습니다(GCC로 테스트한 것과 같은 코드입니다).

#include <vector>

#include <iostream>

#include <time.h>

#include <set>

#include <algorithm>

 

const unsigned N = 3001;

 

extern bool some_test;

 

std::set<int>

get_set(int)

{

    std::set<int> s;

    for (int i = 0; i < N; ++i)

        while (!s.insert(std::rand()).second)

            ;

    if (some_test)

        return s;

    return std::set<int>();

}

 

std::vector<std::set<int> >

generate()

{

    std::vector<std::set<int> > v;

    for (int i = 0; i < N; ++i)

        v.push_back(get_set(i));

    if (some_test)

        return v;

    return std::vector<std::set<int> >();

}

 

float time_it()

{

    clock_t t1, t2, t3, t4;

    clock_t t0 = clock();

    {

    std::vector<std::set<int> > v = generate();

    t1 = clock();

    std::cout << "construction took " << (float)((t1 - t0)/(double)CLOCKS_PER_SEC) << std::endl;

    std::sort(v.begin(), v.end());

    t2 = clock();

    std::cout << "sort took " << (float)((t2 - t1)/(double)CLOCKS_PER_SEC) << std::endl;

    std::rotate(v.begin(), v.begin() + v.size()/2, v.end());

    t3 = clock();

    std::cout << "rotate took " << (float)((t3 - t2)/(double)CLOCKS_PER_SEC) << std::endl;

    }

    t4 = clock();

    std::cout << "destruction took " << (float)((t4 - t3)/(double)CLOCKS_PER_SEC) << std::endl;

    std::cout << "done" << std::endl;

    return (float)((t4-t0)/(double)CLOCKS_PER_SEC);

}

 

int main()

{

    std::cout << "N = " << N << '\n';

    float t = time_it();

    std::cout << "Total time = " << t << '\n';

}

 

bool some_test = true;

 

 

< 결과 >


 


 


이 결과를 보면 생성과 알고리즘을 사용했을 때 많은 차이가 나는 것을 알 수 있습니다.

기존에 STL의 알고리즘을 자주 사용한 경우라면 VC++ 10으로 컴파일만 해도 어느 정도의 성능 향상을 얻을 수 있을 것 같습니다.

 

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[Plus C++0x] 람다(Lambda) 이야기 (마지막회)

C++0x 2010.06.03 00:00 Posted by 비회원
람다 함수 활용(2)

앞선 포스팅에서 람다 함수의 캡쳐(Capture) 기능에 대해 설명 했었습니다. (여기! 있습니다)
캡쳐 기능을 사용 하면 다음과 같이 활용 할 수 있습니다.

④ 템플릿을 대체하는 데 활용
람다 함수의 캡처 기능을 사용하면 람다 함수 몸체에서 외부 변수들을 마음껏 사용할 수 있을 뿐만 아니라 클로저(Closure)가 되어 함께 묶입니다. 이 기능을 활용하면 기존에 파라미터 타입과 개수 처리를 일반화하기 위해 사용하던 템플릿 사용을 지양할 수 있습니다. 아래 두 함수가 템플릿과 람다 함수를 이용해서 원하는 시점에 호출 되게 하려면 어떻게 하면 될까요?

// 템플릿 객체와 람다 함수를 이용해 간접적으로 호출될 예제 함수들


void Function_Arg1(int arg1)

{

cout << "Function_Arg1 : " << arg1 << endl;

}

 

void Function_Arg2(int arg1, const char* arg2)

{

cout << "Function_Arg2 : " << arg1 << "," << arg2 << endl;

}



위 두 함수를 특정 파라미터와 묶어 특정 시점에 호출해서 사용하고 싶을 경우 템플릿을 이용하면 아래와 같은 방법으로 사용할 수 있습니다.

// 템플릿 객체를 이용해 함수를 인자와 함께 묶어 특정 시점에 호출하는 예


int arg1 = 1004;

const char* arg2 = "Lambda!";

 

vector<ITask*> taskList;

 

// 1. 컨테이너에 담는다.

taskList.push_back( new Task_1<void, int>(&Function_Arg1, arg1) );

taskList.push_back( new Task_2<void, int, const char*>(&Function_Arg2, arg1, arg2) );

// 2. 특정 시점에 컨테이너를 순회하며 실행시킨다.

for( auto i = taskList.begin(); i != taskList.end(); ++i )

{

(*i)->Do();

}



위와 같은 코드로 템플릿 객체를 사용하려면 다음과 같은 코드가 필요합니다.

// 파라미터 1개짜리 함수를 담기 위한 템플릿 클래스


template<typename RetType, typename ArgType1>

class Task_1 : public ITask

{

typedef function<RetType(ArgType1)> FunctionType;

 

public:

Task_1(FunctionType f, ArgType1 a1)

: function_(f), arg1_(a1)

{

}

 

virtual void Do()

{

cout << "Task_1::Do()" << endl;

function_(arg1_);

}

private:

FunctionType   function_;

ArgType1       arg1_;

};

 

// 파라미터 2개짜리 함수를 담기 위한 템플릿 클래스

template<typename RetType, typename ArgType1, typename ArgType2>

class Task_2 : public ITask

{

typedef function<RetType(ArgType1,ArgType2)> FunctionType;

 

public:

Task_2(FunctionType f, ArgType1 a1, ArgType2 a2)

: function_(f), arg1_(a1), arg2_(a2)

{

}

 

virtual void Do()

{

cout << "Task_2::Do()" << endl;



실제 C++ 프로젝트를 진행하다 보면 위와 비슷한 형태의 템플릿 클래스를 자주 구현하게 됩니다. 이 때 발생하는 문제점은 함수의 파라미터가 늘어날 때마다 템플릿 클래스를 추가해 줘야 하고 코드가 직관적이지 않다는 점입니다. BOOST_PP를 이용하면 자동화할 수 있지만 디버깅이 굉장히 어렵고 작성자만 이해할 수 있는 코드가 만들어지곤 합니다. 때론 작성자도 이해 못하죠 ;)

그럼 이런 상황에서 람다 함수를 이용하면 어떨까요?
아래 예제를 보면 람다 함수의 캡처 기능을 이용해 깔끔하게 구현되는 것을 볼 수 있습니다. 뿐만 아니라 실행시킬 함수의 파라미터가 몇 개든 타입이 무엇이든 추가되는 코드는 없습니다. 앞으로 많은 부분에서 람다 함수를 이용해 템플릿 사용을 줄일 방안이 제안 되길 기대해 봅니다.


// 람다 함수를 이용해 함수와 인자를 묶어 특정 시점에 호출하는 예제


int arg1 = 1004;

const char* arg2 = "Lambda!"; 

...
...
typedef function<void(void)> LambdaType;


vector<LambdaType> lambdaList;

 

lambdaList.push_back( [=](){ Function_Arg1(arg1); } );

lambdaList.push_back( [=](){ Function_Arg2(arg1, arg2); } );

 

for_each( lambdaList.begin(), lambdaList.end(), [](LambdaType lambda)

{

lambda();

});




마치면서

4회에 걸쳐『Plus C++0x』람다(Lambda) 이야기를 했습니다. 막연히 람다(Lambda) 의 기능에 대해 설명 하기 보다는 이면에 깔린 배경 개념을 소개 함으로써 현대 프로그래밍 언어가 갖는 특징을 이야기 하고 싶었습니다.

다음 시리즈에서는 우측 값 참조(RValue Reference)에 대해 알아보고 C++0x에서 어떤 의미를 갖는지를 설명하려고 합니다. 람다(Lambda) 관련해서는 많은 내용을 한 번에 준비해서 쓰려니 고생스럽더군요. 이번엔 차근차근 포스팅 하면서 글을 완성할 수 있으면 좋겠네요 ;)

( 마이크로소프트웨어 6월호의 『생각의 직관적인 표현, 람다(Lambda)』를 보시면 보다 잘 정리 되고 추가 된 내용을 보실 수 있습니다. )


고자료
1. MSDN -
http://msdn.microsoft.com/en-us/library/dd293608.aspx
2. MSDN - http://msdn.microsoft.com/en-us/library/dd293599.aspx
3. MSDN - http://channel9.msdn.com/posts/kmcgrath/Lambda-Expressions-in-C/
4. MSDN - http://blogs.msdn.com/vcblog/archive/2008/11/18/stupid-lambda-tricks.aspx
5. Wikipedia - http://en.wikipedia.org/wiki/First-class_function
6. Wikipedia -  http://en.wikipedia.org/wiki/Higher-order_function
7. VSTS 2010 Team Blog -  http://vsts2010.net/category/Language%20Development/C++0x


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[Plus C++0x] 람다(Lambda) 이야기 (3)

C++0x 2010.06.01 00:00 Posted by 비회원

람다 함수 활용(1)

지금까지 First-Class Object, Higher-Order Function, Closure와 같은 조금은 지루한 개념들을 살펴봤습니다. 이런 개념들을 설명하지 않으면 람다 함수를 함수 포인터나 함수 객체를 대체하는 Syntactic Sugar로 오해할 수도 있기 때문입니다. 또 어떻게 람다 함수를 일반 변수처럼 사용할 수 있는지 배경 지식을 설명하기 위해서였습니다.

그럼 이 람다 함수를 어디에 사용하면 좋을까요?앞으로 4가지 활용 예를 살펴 보겠습니다.


① 지연 호출(Deferred Call)에 활용
vector와 같은 자료구조에 저장한 후에 필요한 특정 시점에 호출하는 것입니다.
아래 예제에서는 람다 함수를 이용해 Task를 만들고 TaskManager라는 컨테이너 클래스를 통해 특정 시점에 람다 함수가 실행되는 것을 나타내고 있습니다.

TaskManager manager;

 

// TaskManager에 Task로 저장한다.

manager.AddTask( [](){ cout << “task1” << endl;} );

manager.AddTask( [](){ cout << “task2” << endl;} );

 

 

// 같은 스레드 혹은 다른 스레드에서 실행시킨다.

manager.Run();



② 비동기 호출과 결과 코드의 응집성을 높이는 데 활용

비동기 처리 코드의 문제점은 비동기 요청 함수를 호출하는 곳과 결과를 처리하는 함수가 동떨어져 있어 로직 흐름을 파악하기 어렵다는 것입니다. 비동기 요청 함수를 호출할 때 결과 처리에 대한 코드를 람다 함수로 구현해 파라미터로 전달하면 코드 응집성이 높아질 수 있습니다.
아래 예제에서는 Client 클래스의 요청에 대한 처리가 비동기적으로 이뤄질 때 람다 함수를 이용해 요청 시점에 결과를 어떻게 처리할 것인지 기술하는 것을 나타내고 있습니다.

 

class Client

{

public:

// 비동기 요청에 대한 결과를 처리할 람다 함수 타입

typedef function<void(int result)> AsynResultProcessor;

 

public:

void AsyncRequest(AsynResultEvent resultEvent)

{

resultEvent_ = resultEvent;

}

 

void Process()

{

Result_ = 1004;

 

// 미리 저장된 결과 처리 람다 함수를 호출한다.

resultProcessor_(result_);

}

private:

AsynResultProcessor    resultProcessor_;

int                      result_;

};

 

Client client;

 

// 응답을 어떻게 처리할 것인지 요청 시 기술할 수 있다.

client.AsyncRequest( [](int result)

{

// 어떻게 결과를 처리할 것인지 기술

cout << result << endl;

});

 

// 특정 시점에 처리한다.

client.Process();




③ 일회성 함수를 쉽게 구현하기 위해 활용

특히 STL을 이용하는 데 유용합니다. STL 알고리즘 함수들의 입력 파라미터로 람다 함수를 넘겨주면 따로 함수 객체를 정의하는 번거로움이 사라지고 코드 응집성이 높아지므로 STL 함수의 작동을 더 쉽게 이해할 수 있습니다. 아래 예제에서는 함수 객체와 람다 함수 이용을 비교해서 보여주고 있습니다.

 

//  for_each()를 위한 함수 객체

//  일회성 호출을 위해 많은 코드가 필요하고 가독성도 떨어진다.

struct LambdaFunctor

{

void operator()(int n) const

{

cout << n << " ";

}

};

 

int main()

{

vector<int> v;

 

for (int i = 0; i < 10; ++i)

{

v.push_back(i);

}

 

// 1. 함수 객체를 이용한 코드

for_each(v.begin(), v.end(), LambdaFunctor());

 

// 2. 람다 함수를 이용한 코드

for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

 

cout << endl;

}




포스팅이 길어졌네요~
다음 글에서 람다 활용에 대해 마저 이야기 하고 "람다 이야기" 시리즈를 마치도록 하겠습니다.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[Plus C++0x] 람다(Lambda) 이야기 (2)

C++0x 2010.05.27 23:13 Posted by 비회원

지난 글에서 이야기 했던 람다(Lambda)의 배경 개념에 대해 알아보겠습니다.

람다 함수는 First-Class Object?
프로그래밍 언어를 이루는 class, struct, int와 같은 개체들 중에서 아래 조건을 만족하면 First-Class Object로 분류합니다. (First-Class Object는 분류의 한 갈래를 의미하는 것이지 상하 관계를 의미하진 않습니다.)

- 변수와 자료구조에 저장하고 사용할 수 있다.
- 함수의 입력 값으로 사용할 수 있다.
- 함수의 반환 값으로 사용할 수 있다.
- 실행 시간에 생성할 수 있다.

즉, 기존 C++에서는 class, struct, int 등이 First-Class Object 였고  아래 예제에서 볼 수 있듯이 C++0x에서 람다 함수(Lambda Function)가 First-Class Object의 조건들을 만족시킵니다.

...
int main()

{

string text = "C++0x Lambda!";

 

// 1. "코드 조각" 변수에 대입하기

function<void()> lambda = [=]()

{                               

cout << text << endl;

};

 

// 2. "코드 조각"을 자료구조에 저장하기

vector< function<void()> > container;

{               

container.push_back( lambda );

container.push_back( [=](){ cout << text << endl; } );

};

 

// 3. "코드 조각"을 함수의 입력 파라미터로 사용하기

for_each( container.begin(), container.end(), [](const function<void()>& f){ f(); } );

 

return 0;

}


위 조건들을 통해 First-Class Object들은 프로그래밍 언어에서 별 다른 제약 조건 없이 변수로 사용할 수 있게 됩니다. 최근 여러 언어들이 함수를 First-Class Object로 제공하는 이유도 함수를 값 처럼 사용할 수 있게 되면 여러 모로 유용하기 때문입니다. 특히 함수는 다른 First-Class Object들을 생성하거나 조작하는 일련의 코드 묶음이기 때문입니다.

C++0x에서는 람다 함수(Lambda Function)를 First-Class Object로 제공함으로써 코드 조각을 일반 변수처럼 사용할 수 있게 해줍니다.



람다 함수는 Higher-Order Function?

함수의 입력 값으로 함수를 전달 받거나 함수의 결과 값으로 함수를 반환할 수 있을 때 Higher-Order Function이라고 합니다. 아래 예제 코드를 보면 쉽게 알 수 있습니다.

 

int main()

{

// 1. 람다 함수를 반환 값으로 한다.

auto g = [](int x) -> function<int (int)>

{          

return [=](int y) { return x + y; };

};

 

// 2. 람다 함수를 입력 값으로 받는다.

auto h = [](const function<int (int)>& f, int z)

{

return f(z) + 1;

};

 

auto a = h( g(7), 8 );

 

cout << a << endl;

}



예제를 보면 h( g(7), 8 )과 같이 표현된 것을 볼 수 있습니다. 마치 수학 책에서 보던 h( g(x), y )처럼 표현할 수 있는 것입니다. 람다 함수는 C++0x에서 First-Class Object 조건을 만족시키기 때문에 Higher-Order Function의 요구사항 또한 만족시킵니다.


람다 함수는 Closure?
클로저(Closure)는 함수를 호출한 상위 코드 블록의 변수들이 호출된 함수와 묶인 것을 뜻합니다. 즉 호출된 함수는 상위 코드 블록의 외부 변수와 묶여 자기만의 상태를 갖게 되는 것입니다.
C++0x에서는 람다 함수(Lambda Function)가 상위 코드 블록의 변수들과 묶여 클로저(Closure)가 될 수 있다. 이때 람다 함수에서 외부 변수들을 참조하는 것을 ‘캡처’라고 말합니다.
캡처(Capture)는 두 가지 방법으로 할 수 있습니다. 람다 시작을 나타내는 [] 기호 사이에 =과 & 기호를 이용할 수 있습니다. [=]은 값 복사를 의미하고 [&]는 값 참조를 의미합니다. 아래 예제 코드를 보시죠.

 

void capture()

{

int a = 0;

int b = 1;

int c = 2;

 

// 1. default 값 복사 캡처. 상위 코드 블록의 지역 변수 모두 값 복사 가능

[=](){ cout << a << “ “ << b << endl; }();  // 0 1 출력

 

// 2. default 값 참조 캡처. 상위 코드 블록의 지역 변수 모두 값 참조 가능

[&](){ cout << a << “ “ << b++ << endl;}(); // 0 1 출력

 

// 3. default 값 복사 캡처, b와 c 참조 캡처

[=, &b, &c](){ cout << a << “ “ << b << “ ” << c << endl; }();  // 0 2 2 출력

}



이번 포스팅에선 몇 가지 배경 개념들을 알아봤는데요, 도움이 되셨는지 모르겠습니다 ;)
다음 글에선 람다 함수의 활용 방안에 대해 알아보겠습니다.

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[Plus C++0x] 람다(Lambda) 이야기 (1)

C++0x 2010.05.27 21:59 Posted by 비회원

마이크로소프트웨어 6월호에 실릴 C++ 0x 관련 글을 썼습니다.
제목은 『생각의 직관적인 표현, 람다(Lambda)』입니다.  팀 블로그에서는 『Plus C++0x』라는 제목으로 포스팅 하려고 합니다. 기존에 흥배님께서 C++ 0x에 대해 이미 좋은 글들을 많이 쓰셔서 표절하지 않으면서 무엇을 써야 할지 정말 많은 고민을 했답니다. ;)

그래서 약간 Advanced 하면서 불친절하게(?) 특정 주제에 대해 나름대로 재해석해보기로 했습니다. 첫 주제는 람다(Lambda) 로 잡았구요 다음은 우측값 참조(RValue Reference) 에 대해 쓰려고 합니다. 전체적인 아웃라인은 여기를 보시면 됩니다.


C++0x에 함수형 프로그래밍 언어 기능 추가!?
C++를 사용하다가 요즘 인기 있는 Python, C#, Ruby 같은 언어들을 쓰게 되면 무엇보다 직관적이고 편하다는 생각이 듭니다. 이 언어들은 람다(Lambda)와 클로저(Closure) 같은 함수형 프로그래밍 언어의 기능들을 지원함으로써 생각을 보다 직관적이고 효율적인 코드로 표현할 수 있게 합니다.


그렇다면 C++는 어떨까요?
포인터와 함수 객체 그리고 템플릿으로 점철된 C++ 프로그래밍 세계에도 이런 함수형 프로그래밍 언어의 기능들이 추가되면 직관적이고 덜 수고스러운 코딩을 할 수 있을까요?
마침 비주얼 스튜디오 2010이 발표되면서 비주얼 C++ 10가 공개됐습니다. 인텔리센스 기능 향상, 실시간 에러 검사, 병렬 라이브러리와 같은 막강한 기능이 추가됐지만 무엇보다도 C++0x라는 C++의 새로운 표준을 충실히 구현함으로써 C++ 프로그래머의 생산성을 높여 주목을 끌고 있습니다.
특히 C++0x는 언어의 핵심 기능으로 앞서 언급했던 람다(Lambda)와 클로저(Closure)를 기술하고 있고 이를 충실히 구현한 비주얼 C++ 10을 통해 보다 쉽게 사용할 수 있습니다.

이번 『Plus C++0x 람다이야기』를 통해 전달할 핵심 내용은 다음 두 가지 입니다.

C++0x에서 코드 조각(a piece of code)을 일반 변수처럼 사용할 수 있게 됐다.
C++0x에서 클로저(Closure)를 사용할 수 있게 됐다.


아래 코드를 보시죠.

#include <iostream>

#include <vector>

#include <string>

#include <algorithm>

#include <functional>

 

using namespace std;

 

int main()

{

string text = "C++0x Lambda!";

 

// 1. "코드 조각" 변수에 대입하기

function<void()> lambda = [=]()

{                               

cout << text << endl;

};

 

// 2. "코드 조각"을 자료구조에 저장하기

vector< function<void()> > container;

{               

container.push_back( lambda );

container.push_back( [=](){ cout << text << endl; } );

};

 

// 3. "코드 조각"을 함수의 입력 파라미터로 사용하기

for_each( container.begin(), container.end(), [](const function<void()>& f){ f(); } );

 

return 0;

}



위 예제에서 보는 것처럼 이렇게 일반 변수처럼 대입할 수 있고, 자료구조에 저장할 수 있고 함수의 입출력 값으로 사용할 수 있는 코드 조각을 람다(Lambda) 혹은 람다 함수(Lambda Function)라고 합니다.

람다(Lambda)를 이해하고 어떻게 사용하며 활용할지 설명하기 위해 First-Class Object, Higher-Order Function, Closure 같은 프로그래밍 언어 수업 시간에 나올 법한 개념들을 소개하려고 합니다. 개념적인 이해를 통해 람다(Lambda)와 친해져 보시죠 ;)

또 어떻게 사용 할지 몇 가지 사용 패턴을 제안해 볼까 합니다.
(약간 불친절하게 글이 진행됩니다. http://vsts2010.net에 있는C++0x 관련 글들을 먼저 읽어 볼 것을 권장합니다.)


람다 함수는 함수 안에 정의할 수 있는 이름 없는 함수(anonymous function)? 

...

void outer_function()

{

void inner_function()

{

}

}


위 코드를 보면 outer_function() 함수 안에 inner_function() 함수가 정의되어 있습니다. C++에서 이런 문법 사용이 가능할까요? 물론 사용할 수 없습니다. 즉, 함수 안에서 함수를 선언하고 정의할 수 없습니다. 그러나 람다 함수(Lambda Function)는 함수 안에서 선언하고 정의하고 호출할 수 있습니다. 


void outer_function()

{

// 이름 없는 함수! 람다!

[](){}();

}


위 예제에서 아무 동작을 하지 않는 람다 함수가 선언, 정의, 호출 됐습니다.

다음 편에서 이런 동작을 가능하게 하는 개념적 배경인 First-Class Object, Higher-Order Function, Closure 에 대해 알아보도록 하겠습니다.

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

월간 마이크로소프트웨어 'C++ 개발자와 함께하는 Visual Studio 2010' 세미나를 주최합니다.

특히 Visual Studio 2010 과 C++0x 을 중심으로 매우 많은 관심을 보이고 있는 분야입니다. C++ 언어를 이용하여 개발을 하신다면 반드시 참석해야 할 더 없는 좋은 세미나라고 생각합니다.

저희 VIsual Studio 2010 팀에 계시는 NCSoft 의 최성기님께서 윈도우 7 개발 부분에서 발표를 하게 되었습니다.

세미나 신청은 아래의 링크를 통해 신청하시기 바랍니다. 많은 참석 부탁 드립니다.
http://new.imaso.co.kr/seminars/vs2010/agenda/

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

"Visual C++ 10과 C++0x" pdf 파일

C++0x 2010.04.20 13:18 Posted by 흥배

C++0x 관련 책 "Visual C++ 10 C++0x"가 오늘 한국 MSDN 사이트에 올라왔습니다.

e-book으로 보기를 원하는 분이나 책을 얻지 못한 분들은 다운로드 해서 보세요

 

MSDN : http://msdn.microsoft.com/ko-kr/default.aspx


 


Visual Studio의 시작 페이지에도 다운로드 링크가 표시됩니다.

 

 

그리고 책에 오타가 있습니다.

48페이지 decltype 설명에서 오타가 있습니다.




팀블로그에 예전에 RValue Reference lambda 관련 글을 올린 적이 있는데 책을 만들 때 제가 올린 글을 다시 보니 설명에 미흡한 부분이 많아서 RValue Reference lambda는 새로 적었습니다. 그러니 RValue Reference lambda를 공부할 때는 꼭 블로그에 올라와 있는 글보다 이 책의 글을 보시기 바랍니다.


저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

C++0x 관련 책 "Visual C++ 10과 C++0x"

C++0x 2010.04.17 08:30 Posted by 흥배

4월 15일에 열린 “C++ 개발자/게임개발자를 위한 VS2010 세미나에서 제가 이때까지 VSTS 2010 팀블로그에 올린 C++0x와 관련된 글을 작은 책으로 만든 것이 마지막에 참석하신 분들에게 배포 되었습니다.

 

원래는 pdf 파일로만 공개될 줄 알았는데 작은 분량이지만 이렇게 책으로 나왔습니다. 생각 이상으로 책 편집도 잘 되었더군요. 책 내용은 팀블로그에 올린 글을 좀 더 다듬었습니다만 RValue Reference lambda는 제가 올린 글이 좋지 못해서 새롭게 적었습니다.

 

제가 글을 아직은 잘 적는 편은 아니라서 글이 매끄럽지 못할 수도 있습니다만 좋게 봐 주시기를 바랍니다.^^;

이 책에 나온 것만 아시면 VC++ 10에 추가된 C++0x의 기능을 사용하기에는 전혀 문제가 없다고 생각합니다. 꼭 도움이 되었으면 합니다.


이번 세미나를 위해 한국에 오신 VC++ 제품의 PM인 Ulzii씨의 싸인도 받았습니다.^^

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License


 

티스토리 툴바