[STL] 8. <algorithm>에 추가된 새로운 함수들 - partition_copy

C++0x 2011. 4. 12. 09:00 Posted by 알 수 없는 사용자

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

 

VC++ 10에 새롭게 추가된 STL. 다시 시작합니다

C++0x 2011. 4. 11. 09:00 Posted by 알 수 없는 사용자

작년 가을 무렵에 방수철님이 VC++ 10에 새롭게 추가된 STL 라이브러리를 잘 설명해 주셨는데 아쉽게 마무리를 짓지 못했습니다. 그래서 앞으로 제가 이것을 마무리 지으려고 합니다.

 

먼저 방수철님이 올린 글을 보지 못하신 분들이나 또는 이제 기억 속에 남아 있지 않는 분들은 다시 한번 글을 찬찬히 봐 주시기 바랍니다.^^

봐야 할 글들은 아래와 같습니다. 모두 길지 않고 내용도 어렵지 않으니 STL을 아는 분들이라면 금방 볼 수 있는 글들입니다.

[STL] 1. What's new in VC++ 2010?

[STL] 2. unique_ptr (1/2)

[STL] 3. unique_ptr (2/2)

[STL] 4. make_shared

[STL] 5. <algorithm> 추가된 새로운 함수들 (1/5)

[STL] 6. <algorithm> 추가된 새로운 함수들 all_of, any_of, none_of (2/5)

[STL] 7. <algorithm> 추가된 새로운 함수들 copy_if, copy_n, find_if_not (3/5)

 

그럼 저는 위의 글들을 다 보셨다고 생각하고 이 글들을 이어서 다음 내용을 포스팅 하겠습니다. 금방 올라가니 조금만 기다려주세요^^

안녕하세요. 방수철입니다.
이번 글에서는 copy_if, copy_n, find_if_not에 대해서 알아보도록 하겠습니다.

copy_if

MSDN에 등록된 copy_if에 대한 선언은 다음과 같습니다.

template<class InputIterator, class OutputIterator, class BinaryPredicate>
    OutputIterator copy_if(
        InputIterator_First, 
        InputIterator _Last,
        BinaryPredicate _Comp
    );

copy_if는 _First 가 가리키는 위치부터 _Last가 가리키는 위치까지 _Comp조건을 만족하는 element를 copy하게 됩니다.
그런데 뭔가 허전하죠? 이 선언에는 copy를 수행해서 저장될 container가 빠져있습니다. 아마 MSDN의 오류인 것 같습니다.
copy인 만큼 당연히 source가 되는 container에는 변화가 없습니다.

VC2010에 포함된 헤더파일에 있는 선언은 다음과 같습니다.

template<class _InIt,
	class _OutIt,
	class _Pr> inline
	_OutIt copy_if(_InIt _First, _InIt _Last, _OutIt _Dest,
		_Pr _Pred)

_Dest 인자가 선언되어 있는 것을 확인할 수 있습니다.

아래 코드는 20의 약수를 골라내는 프로그램입니다.

	const int N=12;
	list<int> src;
	list<int> dest;
	// 데이터 초기화
	for(int i=1 ; i<=N ; i++)
		src.push_back(i);
 
	// 약수만을 복사
	copy_if(src.begin(), src.end(), back_inserter(dest), [=](int n){ return N % n == 0; });
 
	// 결과 출력
	cout<<N<<"의 약수 : ";
	for_each(dest.cbegin(), dest.cend(), [](int n){cout << n << " ";});



copy_if는 내부적으로 _Dest 반복자에 ++ 연산을 해서 위치를 이동시킵니다. 따라서 dest list는 미리 충분한 공간을 확보해두거나
예로든 코드 처럼 back_inserter로 반복자를 넘겨주어야 합니다.



copy_n

MSDN에 등록된 copy_n에 대한 선언은 다음과 같습니다.

template<class InputIterator, class Size, class OutputIterator>
    OutputIterator copy_n(
        InputIterator  _First, 
        Size _Count,
        OutputIterator _Dest
    );


copy_n은 _First 위치에서 _Count 개수만큼 element를 _Dest에 복사합니다. 다음과 같이 사용할 수 있습니다.

  1 	copy_n(src.begin(), 3, back_inserter(dest));

find_if_not

MSDN에 등록된 find_if_not에 대한 선언은 다음과 같습니다.


template<class InputIterator, class Predicate>
    InputIterator find_if_not(
        InputIterator _First, 
        InputIterator _Last,
        BinaryPredicate _Comp
    );



_First 위치부터 _Last위치까지 _Comp를 수행해서 false를 반환하는 첫번째 위치의 반복자를 return하게 됩니다.
아래의 프로그램은 copy_if에서 사용한 프로그램을 약간 수정한 것입니다.
12의 약수가 아닌 가장 작은 자연수를 구하는 과정입니다.

	const int N=12;
	list<int> src;
	list<int> dest;
	// 데이터 초기화
	for(int i=1 ; i<=N ; i++)
		src.push_back(i);
 
	// 약수만을 복사
	copy_if(src.begin(), src.end(), back_inserter(dest), [=](int n){ return N % n == 0; });
	// 3개만 복사
	auto pos = find_if_not(src.cbegin(), src.cend(), [=](int n){ return N % n == 0; });
 
 
	// 결과 출력
	cout<<N<<"의 약수 : ";
	for_each(dest.cbegin(), dest.cend(), [](int n){cout << n << " ";});
	cout<<endl;
	cout<<N<<"의 약수가 아닌 가장 작은 자연수 : "<< *pos <<endl;


출력은 다음과 같습니다.


RValue Reference에 의한 STL의 성능향상 테스트

C++0x 2010. 10. 18. 07:00 Posted by 알 수 없는 사용자

트위터에서 @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으로 컴파일만 해도 어느 정도의 성능 향상을 얻을 수 있을 것 같습니다.

 

VS2010에서 nullptr의 알려진 버그

C++0x 2010. 9. 30. 23:57 Posted by 알 수 없는 사용자
안녕하세요.
이 글에서는 VS2010에서 nullptr의 버그를 간단하게 언급하겠습니다.
아래 코드는 문제 없이 컴파일 되고 실행이 됩니다.

#include "stdafx.h"


template<int> void f(){};

void g()
{
    f<nullptr>();
}

template<template <class> class> class C {};

void test()
{
    auto x = nullptr;
    x++; // should give error
    x += 1; // should give error
}

 int _tmain(int argc, _TCHAR* argv[])
{
    C<nullptr> c;
    test();

    return 0;
}


하지만 다음과 같은 세가지 버그가 존재합니다.

1. nullptr이 함수 템플릿의 non-type argument로서 사용이 됩니다. ( f<nullptr>(); )
2. nullptr이 template template argumen로서 사용이 됩니다. ( template<template <class> class> class C {}; )
3. nullptr에 대해서 몇몇 수학적 operation이 동작합니다. ( in test() )

안녕하세요. 방수철입니다.
다들 즐거운 한가위를 보내셨는지 궁금하세요.
저는 여러모로 바쁜일이 추석 직전까지 있어서 정신 없이 시간을 보내고 푹 쉬다가
다시 글을 쓰게 됬습니다. 이전 글과 비교해서 너무 오래동안 글이 없었네요.
혹여 기다리신 분들이 있다면 죄송합니다.

이번 글에서는 all_of, any_of, none_of 에 대해서 설명하겠습니다.
이 세가지 함수는 VS2010에서 새로이 <algorithm> 에 추가되었습니다.
이 함수들은 반복자로 탐색이 가능한 구조체에서 특정 조건을 만족하는 원소가 있는지 확인하는 기능을 합니다.

template <class InputIterator, class Predicate>
  bool none_of(InputIterator first, InputIterator last, Predicate pred);

template <class InputIterator, class Predicate>
  bool any_of(InputIterator first, InputIterator last, Predicate pred);

template <class InputIterator, class Predicate>
  bool all_of(InputIterator first, InputIterator last, Predicate pred);

위와 같이 정의된 함수들은 똑같이 세가지 인수를 입력으로 받습니다.

first : 조건을 만족하는지 시험할 범위의 시작 위치의 반복자
last : 조건을 만족하는지 시험할 범위의 마지막 위치의 반복자
pred : 조건을 만족하는지 시험할 때에 사용되는 function 객체. 반드시 한개의 인수만을 전달받아야 하며 true또는 false를 반환해야 한다.

이렇게 인수가 주어질때에 각각의 함수는 다음과 같이 반환하게 됩니다.

all_of : 주어진 범위에서 모든 원소들이 조건을 만족하면 true, 그렇지 않으면 false를 반환한다.
any_of :
주어진 범위에서 한개 이상의 원소들이 조건을 만족하면 true, 그렇지 않으면 false를 반환한다.
none_of :
주어진 범위에서 모든 소들이 조건을 만족하지 않으면 true, 그렇지 않으면 false를 반환한다.

다음의 코드는 numbers라는 배열의 값을 여러 조건식으로 all_of, any_of, none_of를 실행시켜본 것이다.

    array<int,5> numbers = {2, 3, 4, 6, 7};

    auto isPositive = [](int n) { return n > 0; };
    auto isNegative = [](int n) { return n < 0; };
    auto isZero     = [](int n) { return n == 0; };
    auto isEven     = [](int n) { return n % 2 == 0; };
    auto isOdd      = [](int n) { return n % 2 != 0; };

    array<function<bool(int)>, 5> func = {isPositive, isNegative, isZero, isEven, isOdd};
    array<std::string, 5> desc = {"isPositive",
                                  "isNegative", 
                                  "isZero    ", 
                                  "isEven    ", 
                                  "isOdd     " };

    cout<<"The list is { ";
    for_each(numbers.begin(), numbers.end(), [](int n){ cout<<n<<", ";});
    cout<<"}"<<endl;

    cout << "\t\tall_of\tany_of\tnone_of"<<endl;

    for(int i = 0 ; i<5 ; ++i )
    {
        cout << desc[i].c_str() << "\t";
        cout << all_of(numbers.begin(), numbers.end(), func[i]) << "\t";
        cout << any_of(numbers.begin(), numbers.end(), func[i]) << "\t";
        cout << none_of(numbers.begin(), numbers.end(), func[i]) << "\t";
        cout << endl;
    }


아래는 실행 결과입니다.


위의 코드에서 조건식을 변환해본다거나, 주어지는 숫자의 배열에 변화를 줘가면서 테스트를 해보면 더 쉽게 이해 될 것 입니다.

[STL] 5. <algorithm>에 추가된 새로운 함수들 (1/5)

C++0x 2010. 8. 31. 12:00 Posted by 알 수 없는 사용자

안녕하세요. 방수철입니다.
이번 글부터는 <algorithm> 에 추가된 기능들에 대해서 설명드리겠습니다.

STL 알고리즘 일반론

 STL 알고리즘은 다양한 자료구조에 적용할 수 있기 때문에 일반화 되어있다고 합니다. vector나 list같은 STL에 포함된 자료 구조뿐만 아니라 특정 알고리즘의 요구조건을 충족시키는 자료구조나 배열 또한 다룰 수 있습니다. STL 알고리즘은 반복자를 통해 간접적으로 자료구조의 각 요소에 접근함으로써 이런 수준의 일반성을 달성합니다.

 STL 알고리즘은 일반적으로 시작이나 끝 위치로 특정되는 반복자를 처리하게 되는데 참조되는 범위들은 반드시 유효해야 합니다. 또한 범위의 모든 위치는 반드시 역참조가능 해야하며, 각 범위의 역속된 요소들에서 처음부터 마지막 위치까지 반드시 증가 연산으로 도달 가능하여야 합니다.

 STL 알고리즘은 각 STL 자료구조의 연산자와 멤버 함수에 의해 지원되는 동작들을 확장하고, 동시에 다른 종류의 자료구조들 간에 동작하도록 합니다. 알고리즘의 목적을 전달하기 위해서 두가지 접미사가 사용되고 있습니다.

  • '_if' 접미사는 해당 알고리즘이 함수 객채와 같이 사용되는 것을 알려줍니다. 예를 들어 find와 find_if를 비교해보겠습니다. find는 조건으로 주어지는 값과 일치하는(설명의 편의상 일치하는 경우를 예를 들었습니다.) 요소를 찾는 알고리즘 입니다. 하지만 find_if를 사용할 경우 함수 객체를 인수로 전달하여, 그 함수에 의해서 참으로 처리되는 요소를 찾아 낼 수 있습니다. ( 예 : 짝수 찾기 )
  •  '_copy' 접미사는 해당 알고리즘이 자료 구조의 요소들을 다룰 뿐만 아니라 변경된 값을 목적 범위에 복사함을 알려줍니다. 예를 들어, reverse 알고리즘은 주어진 범위의 값들의 순서를 뒤집는 기능을 하지만 reverse_copy 알고리즘은 reverse의 기능과 함께 주어진 반복자의 위치에 뒤집힌 순서의 자료구조를 복사하게 됩니다.

 STL 알고리즘은 종종 목적이나 필요조건을 가리키는 그룹으로 분류됩니다. 그 중 한가지 분류법으로, 자료 구조의 내용이 변경되는 것과 그렇지 않은 것으로 나누는 것 입니다.

  • '변형시키는 알고리즘'은 값은 바꾸지 않지만 순서를 바꾸는 알고리즘입니다.
  • '제거하는 알고리즘'은 일정 범위의 요소들을 지우게 됩니다.
  • '정렬 알고리즘' 범위안의 요소들을 다양한 방법으로 재배치 하며,
  • '정렬된 범위 알고리즘'은 정렬된 범위에서만 동작하는 알고리즘 입니다.

 참고로, 수치 연산을 제공하는 STL 알고리즘은 따로 <numeric>이라는 헤더 파일이 존재하며, 함수 객체와 어댑터가 정의된 <functional> 헤더 파일이 있습니다. boolean을 반환하는 함수 객체는 predicate로 알려져 있습니다. 기본 바이너리 predicate는 비교연산자 < 이다. 일반적으로,  정렬되는 요소들은 대소비교가 가능하여 동등함이 판별될 수 있어야 합니다.

 다음 글들에서는 새로이 구체적으로 추가된 기능들을 몇 차례에 걸쳐 소개하도록 하겠습니다.

[STL] 4. make_shared

C++0x 2010. 8. 12. 12:00 Posted by 알 수 없는 사용자

안녕하세요. 방수철입니다.
이번 글에서는 make_shared 에 대해서 설명드리겠습니다.

VS2010 C++에서는 다음과 같은 구문을 사용할 수 있게 되었습니다.


  1 auto sp = make_shared<int>(1);

이전 버전들에서 위와 같은 기능을 하는 코드는 다음과 같습니다.


  1 shared_ptr<int> sp(new int(1));

make_shared<T>를 사용하는 것이 여러 장점이 있습니다.
첫째, 편리함. 위의 예제에서 처럼 자료형의 이름을 한번이라도 덜 입력해도 됩니다.
두번째, 안정성. 포인터와 객체가 동시에 생성되기 때문에 익히 알려줘 왔던 shared_pointer의 unnamed leak를 방지할 수 있습니다.
마지막으로, 효율성. 이전 버전에서 처럼 shared_ptr을 선언하면 동적할당이 두 번 일어나게 되지만, make_shared<T>를 사용하게 되면 한 번만 일어나게 됩니다.

참고 :
http://msdn.microsoft.com/en-us/library/ee410595.aspx
        

[STL] 3. unique_ptr (2/2)

C++0x 2010. 8. 4. 12:00 Posted by 알 수 없는 사용자

안녕하세요. 지난 글에 이어 unique_ptr에 대해서 소개드리겠습니다.

auto_ptr의 완벽한 대체자 혹은 그 이상 

앞선 글에서 설명드렸듯이 unique_ptr은 auto_ptr이 deprecation으로 결정되면서 그 대체자 로서 제안되게 되었습니다. 필연적으로 auto_ptr의 모든 기능을 포함하고 있으며, 문법 또한 같습니다. 단, auto_ptr이 deprecation으로 결정되게 된 원인이었던 복사 문법을 제외됩니다.
아래 예제 코드는 auto_ptr의 기본적인 동작이 unique_ptr에서도 구현되어 있음을 보여줍니다.

  1     // 기본 생성자
  2     auto_ptr<int> ap;
  3     unique_ptr<int> up;
  4     // 포인터 생성자
  5     auto_ptr<int> autoPtr(new int);
  6     unique_ptr<int> uniquePtr(new int);
  7     // 역참조
  8     *autoPtr = 1;
  9     *uniquePtr = 2;
 10     // 초기화
 11     autoPtr.reset();
 12     uniquePtr.reset();

하지만 아래와 같은 문법에서 차이가 생깁니다.


  1 	auto_ptr<int> ap1(new int);
  2 	auto_ptr<int> ap2 = ap1; // OK
  3 	unique_ptr<int> up1(new int);
  4 	unique_ptr<int> up2(up1); // 컴파일 에러
  5 	unique_ptr<int> up2 = up1; // 컴파일 에러

 이 코드를 실행시켜보면, 4번째 줄에서 lvalue로 부터의 복사 생성자에서 에러가 발생합니다. 또한 5번째 줄에서 unique_ptr에 대한 복사 문법에 대한 컴파일 에러가 발생하는 것을 볼 수 있을 겁니다. 위의 코드를 올바르게 수정하면 다음과 같습니다.

  1 	unique_ptr<int> up1(new int);
  2 	unique_ptr<int> up2 = move(up1); // OK

삭제자(deleter) 지정하기

 특정한 삭제자 함수를 지정하지 않고 unique_ptr을 선언하는 경우에, unique_ptr은 객체가 new로 생성되었을 것을 가정하고 delete를 호출합니다. 하지만 unique_ptr을 선언할 때에 삭제자 함수를 특정함으로써 이를 바꿀 수 있습니다. 예를 들어, malloc으로 선언된 객체는 delete가 아닌 free로 삭제해야 합니다. free를 삭제자로 지정하는 방법은 아래와 같습니다.

  1 	int* intPtr = (int*)malloc(sizeof(int));
  2 	unique_ptr<int, void (*)(void*)> deleterTest(intPtr, std::free);

배열 객체 다루기

 unique_ptr에서는 배열 객체를 다룰 수 있도록 인터페이스를 추가하였습니다. 다음의 코드를 먼저 보겠습니다.


  1 	unique_ptr <int[]> intarr (new int[4]); //array version of unique_ptr
  2 	intarr[0]=10;
  3 	intarr[1]=20;
  4 	intarr[2]=30;
  5 	intarr[3]=40;

 intarr 객체는 int의 배열을 가지는 unique_ptr입니다. 하지만 [] 연산자를 구현하여 저장된 배열에 직접 접근할 수 있는 인터페이스를 구현되어 있습니다. 이 경우에, 선언부(줄 1)의 template에 들어간 []가 배열 객체를 다루는 unique_ptr임을 알려주게 됩니다.
 또한 위의 삭제자 지정하기에서 기본 삭제자가 delete가 된다고 했는데, 배열 객체를 다루는 unique_ptr은 delete []가 기본 삭제자가 됩니다.

참고 :
http://msdn.microsoft.com/en-us/library/ee410601.aspx
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=401
http://www.devx.com/cplus/10MinuteSolution/39071/1954


[STL] 2. unique_ptr (1/2)

C++0x 2010. 7. 27. 09:00 Posted by 알 수 없는 사용자


안녕하세요. 이번 글에서는 unique_ptr에 대해서 소개드리겠습니다.

Remind

주요 변경 사항으로 소개되 내용에서, unique_ptr에 대해서 다음과 같이 설명하고 있습니다.

"auto_ptr 클래스보다 더 안전한 스마트 포인터형인 unique_ptr 클래스의 구현에도 Rvalue reference가 사용되었습니다. unique_ptr 클래스는 move는 할 수 있지만 copy는 불가능하며, safety에 영향을 미치지 않으면서 강한 소유 의미(strict ownership semantics)를 구현했습니다. 또한, unique_ptr 클래스는 rvalue references가 구현된 container들과 잘 동작합니다."

unique_ptr in MSDN Library

MSDN Library에 소개된 unique_ptr class에 관한 설명입니다.

- 소유하는 객체에 대한 포인터를 저장한다. 해당 객체는 다른
unique_ptr에 의해서 소유될 수 없다. 해당 객체는 unique_ptr이 해제될 때에 해제된다. 다음은 MSDN에 게시되어 있는 class 정의 코드입니다.

  1 template<class Type, class Del = default_delete<Type> >
  2     
class unique_ptr {
  3
  4
public:
  5         
typedef Type element_type;
  6         
typedef Del deleter_type;
  7         
typedef T1 pointer;
  8
  9         
unique_ptr ();
 10         
unique_ptr (
 11             
nullptr_t _Nptr
 12         
);
 13         
explicit unique_ptr (
 14             
pointer _Ptr
 15         
);
 16         
unique_ptr (
 17             
pointer _Ptr,
 18             
typename conditional<
 19                 
is_reference<Del>::value, 
 20                 
Del,
 21                 
typename add_reference<const Del>::type
 22             
>::type _Deleter
 23         
);
 24         
unique_ptr (
 25             
pointer _Ptr,
 26             
typename remove_reference<Del>::type&& _Deleter
 27         
);
 28         
unique_ptr (
 29             
unique_ptr&& _Right
 30         
);
 31         
template<class Type2, Class Del2>
 32             
unique_ptr (
 33                 
unique_ptr<Type2, Del2>&& _Right
 34             
);
 35
 36     
~unique_ptr ();
 37
 38     
unique_ptr& operator= (
 39         
unique_ptr&& _Right
 40     
);
 41     
template<class Type2, Class Del2>
 42         
unique_ptr& operator= (
 43             
unique_ptr<Type2, Del2>&& _Right
 44         
);
 45     
void swap (
 46         
unique_ptr& _Right
 47     
);
 48     
pointer release ();
 49     
void reset (
 50        
pointer _Ptr = pointer()
 51     
);
 52
 53     
pointer get () const;
 54     
Type& operator* () const;
 55     
pointer operator-> () const;
 56     
Del& get_deleter ();
 57     
const Del& get_deleter () const;
 58     
explicit operator bool () const;
 59
 60     
unique_ptr(
 61         
const unique_ptr& _Right
 62
) = delete;
 63     
unique_ptr& operator=(
 64         
const unique_ptr& _Right
 65
) = delete;
 66
 67
private:
 68     
pointer stored_ptr;    // exposition only
 69     
Del stored_deleter;    // exposition only
 70     
};



왜 auto_ptr은 deprecation이 되었을까?
  C++ 0x에서 auto_ptr은 deprecation으로 결정되었습니다. C++ 0x에서는 최소한 auto_ptr과 같은 효율을 가지며 move semantics를 지워하는 unique_ptr을 추가하게 됩니다. 그렇다면 auto_ptr은 왜 deprecation이 되었을까요? 그 이유에 대해서 설명드리겠습니다.

  역사적으로 이전 버전의 C++ 표준에서, 반복된 수정과 패치를 통해서 주요 확장에서 auto_ptr은 안정성을 확보했습니다. 하지만 설계상의 심각한 결함을 극복하지는 못했습니다. 대표적으로 generic 알고리즘과 auto_ptr을 함께 사용할 때에 문제가 되었습니다. generic 알고리즘에서는 복사 연산 문법은 정말로 copy 연산이 일어난다는 것을 가정합니다. 하지만 auto_ptr에서 복사 연산자는 실제로 move와 같이 동작합니다. 이런 근본적인 차이 때문에 알고리즘의 구현에 따라서 원하는 결과를 얻지 못 할 수도 있었습니다.

 정렬 알고리즘을 통해서 예를 들겠습니다. 버블 정렬이나 선택 정렬에서는 하나의 지역변수에 한 원소를 골라서 복사를 하는 방식을 사용합니다. 이런 경우에는 완벽하게 유효한 구현이 됩니다. 하지만 빠른 정렬의 경우를 생각해보면, 구현 중에 다음과 같은 코드가 있을 겁니다.

  value_type pivot = *mid_point;
 이 경우에 알고리즘상 복사 연산자에서 실제로 복사가 일어남을 전제하는 알고리즘인 겁니다. 하지만 auto_ptr은 복사 표현을 통해서 move를 구현했기 때문에 이런 구현에서는 sort()의 결과가 안전할 것인지 보장 할 수가 없었습니다.

 수년간의 논의 끝에, C++ 표준 위원회는 auto_ptr은 deprecated로 결정했습니다. 하지만 auto_ptr의 다른 모든 장점을 수용할 수 있으며 더 안전하고 명확한 인터페이스를 C++ 0x에 추가 될 필요가 있었고, 그 대채자가 unique_ptr 입니다. unique_ptr은 복사 생성자를 private으로 선언합니다. 따라서 generic 알고리즘을 unique_ptr과 같이 사용하여 호출할 때에, 모든 generic 코드는 컴파일 타임에 에러가 나거나 그렇지 않다면 완벽하게 유효한 결과를 내게 됩니다.

 다음 글에서는 unique_ptr의 예제 코드 위주로 auto_ptr과의 차이점과 개선점을 살펴보도록 하겠습니다.

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

[STL] 4. make_shared  (8) 2010.08.12
[STL] 3. unique_ptr (2/2)  (0) 2010.08.04
[STL] 1. What's new in VC++ 2010?  (0) 2010.06.07
[Plus C++0x] 람다(Lambda) 이야기 (마지막회)  (2) 2010.06.03
[Plus C++0x] 람다(Lambda) 이야기 (3)  (0) 2010.06.01

[STL] 1. What's new in VC++ 2010?

C++0x 2010. 6. 7. 00:00 Posted by 알 수 없는 사용자

소개
안녕하세요. 이번에 팀블로그에 합류하게 된 방수철이라고 합니다.
저는 VC++ 2010에서 구현된 Standard C++ Library에 대해서 소개를 드리고자 합니다.
이 글에서는 간단하게 어떤 변화가 있었는지 개요를 설명드리고, 이 후의 글에서 하나씩 집어보려 합니다.


Standard C++ Library의 변경사항

  • STL의 많은 함수에서 move semantics와 perfect fowarding을 구현하기 위해서 rvalue reference를 사용하였습니다. Move semantics와 perfect fowarding은 변수나 매개변수의 메모리 할당이나 변수 할당의 성능을 크게 향상시킵니다.

  • auto_ptr 클래스보다 더 안전한 스마트 포인터형인 unique_ptr 클래스의 구현에도 Rvalue reference가 사용되었습니다. unique_ptr 클래스는 move는 할 수 있지만 copy는 불가능하며, safety에 영향을 미치지 않으면서 강한 소유 의미(strict ownership semantics)를 구현했습니다. 또한, unique_ptr 클래스는 rvalue references가 구현된 container들과 잘 동작합니다.
  • <memory>헤더에 포함된, 새로운 make_shared 함수는 어떤 객체가 만들어지는 동시에 shared pointer를 만들기에 편리하고, 안전하며, 효율적입니다.

  • find_if_not, copy_if, is_sorted 와 같은 15개의 새로운 함수가 <algorithm>에 추가되었습니다.

  • 단반향으로 연결된 리스트(single linked list)가 <forward_list> 헤더에 의해서 지원됩니다.

  • cbegin, cend, crbegin, crend 멤버 함수는 container에서 앞으로 또는 뒤로 움직이는 const_iterator를 제공합니다.

  • <system_error> 헤더와 관련된 템플릿은 저수준 시스템 오류를 처리하는 것을 지원합니다.

  • exception_ptr 클래스의 멤버들은 쓰레드 간에 예외를 전송하기 위해 사용될 수 있습니다.

  • <codecvt> 헤더는 유니코드 문자의 다양한 인코딩을 다른 인코딩으로 변환하는 것을 지원합니다.

  • <allocators> 헤더는 node기반의 container에 대한 메모리 블럭의 할당과 해제를 돕는 여러 템플릿을 정의합니다.

  • <random> 헤더에 많은 업데이트가 있습니다.

다음 글들에서는 각각에 대해서 좀 더 자세히 소개하도록 하겠습니다.

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

[STL] 3. unique_ptr (2/2)  (0) 2010.08.04
[STL] 2. unique_ptr (1/2)  (0) 2010.07.27
[Plus C++0x] 람다(Lambda) 이야기 (마지막회)  (2) 2010.06.03
[Plus C++0x] 람다(Lambda) 이야기 (3)  (0) 2010.06.01
[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27

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

C++0x 2010. 6. 3. 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


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

[STL] 2. unique_ptr (1/2)  (0) 2010.07.27
[STL] 1. What's new in VC++ 2010?  (0) 2010.06.07
[Plus C++0x] 람다(Lambda) 이야기 (3)  (0) 2010.06.01
[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27
[Plus C++0x] 람다(Lambda) 이야기 (1)  (0) 2010.05.27

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

C++0x 2010. 6. 1. 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;

}




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


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

C++0x 2010. 5. 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 출력

}



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

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

C++0x 2010. 5. 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 에 대해 알아보도록 하겠습니다.

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

C++0x 2010. 4. 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를 공부할 때는 꼭 블로그에 올라와 있는 글보다 이 책의 글을 보시기 바랍니다.


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

[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27
[Plus C++0x] 람다(Lambda) 이야기 (1)  (0) 2010.05.27
C++0x 관련 책 "Visual C++ 10과 C++0x"  (9) 2010.04.17
VC++ 10에 구현된 C++0x의 코어 언어 기능들  (1) 2010.04.12
nullptr  (2) 2010.01.28

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

C++0x 2010. 4. 17. 08:30 Posted by 알 수 없는 사용자

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

 

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

 

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

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


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

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

[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27
[Plus C++0x] 람다(Lambda) 이야기 (1)  (0) 2010.05.27
"Visual C++ 10과 C++0x" pdf 파일  (4) 2010.04.20
VC++ 10에 구현된 C++0x의 코어 언어 기능들  (1) 2010.04.12
nullptr  (2) 2010.01.28

VC++ 10에 구현된 C++0x의 코어 언어 기능들

C++0x 2010. 4. 12. 08:30 Posted by 알 수 없는 사용자

Visual C++ 팀 블로그에 C++0x Core Language Features In VC10: The Table라는 이름으로 C++0x의 기능 중 코어 언어와 관련된 것 중에서 VC++ 10에 구현된 것들을 테이블 표로 정리되어 있습니다.

GCC C++0x 구현 항목 테이블 표 형식을 차용했다고 하네요.


 

 

위의 테이블 표에서는 C++0x가 처음 구현된 VC++9VC++ 10을 비교하고 있습니다.

 

그리고 글의 마지막에 작년에 Boost Con(Boost 라이브러리 관련 행사)에서 발표한 자료가 첨부 파일로 있습니다. 이 문서를 보면 VC++ 10에서 구현한 C++0x의 코어 언어 기능들을 설명하고 있습니다.

 

문서를 보니 큰 기능들은 제가 작년부터 공부하면서 저희 팀 블로그나 여러 장소에서 설명 하였지만 일부 기능은 저도 미쳐 파악 하지 못한 것들도 있더군요. 앞으로 이런 빠진 부분에 대해서 팀 블로그를 통해서 설명해 드리겠습니다.^^


 

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

[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27
[Plus C++0x] 람다(Lambda) 이야기 (1)  (0) 2010.05.27
"Visual C++ 10과 C++0x" pdf 파일  (4) 2010.04.20
C++0x 관련 책 "Visual C++ 10과 C++0x"  (9) 2010.04.17
nullptr  (2) 2010.01.28

nullptr

C++0x 2010. 1. 28. 09:00 Posted by 알 수 없는 사용자

오랜만에 팀 블로그에 C++0x 관련 글을 올립니다.

이미 알고 계시겠지만 Visual Stuido 2010 Beta2에 새로운 C++0x 기능이 추가 되었습니다.

추가된 것은 nullptr 이라는 키워드 입니다.

nullptr C++0x에서 추가된 키워드로 널 포인터(Null Pointer)를 나타냅니다.

 

 

null_ptr이 필요한 이유

 

C++03까지는 널 포인터를 나타내기 위해서는 NULL 매크로나 상수 0을 사용하였습니다.

그러나 NULL 매크로나 상수 0을 사용하여 함수에 인자로 넘기는 경우 int 타입으로 추론되어 버리는 문제가 발생 합니다.

 

< List 1 >

#include <iostream>

 

using namespace std;

 

void func( int a )

{

cout << "func - int " << endl;

}

 

void func( double *p )

{

cout << "func - double * " << endl;

}

 

int main()

{

func( static_cast<double*>(0) );

                 

func( 0 );

  func( NULL );

                 

getchar();

return 0;

}

 

< 결과 >

 


첫 번째 func 호출에서는 double* 로 캐스팅을 해서 의도하는 func이 호출 되었습니다. 그러나 두 번째와 세 번째 func 호출의 경우 func( doube* p ) 함수에 널 포인터로 파라미터로 넘기려고 했는데 의도하지 않게 컴파일러는 int로 추론하여 func( int a )가 호출 되었습니다.

 

바로 이와 같은 문제를 해결하기 위해서 nullptr 이라는 키워드가 생겼습니다.

 

 

 

nullptr 구현안

 

C++0x에서 nullptr의 드래프트 문서를 보면 nullptr은 아래와 같은 형태로 구현 되어 있습니다.

 

const class {

public:

    template <class T>

    operator T*() const

    {

        return 0;

    }

 

    template <class C, class T>

    operator T C::*() const

    {

        return 0;

    }

 

private:

    void operator&() const;

 

} nullptr = {};

 

 

 

nullptr 사용 방법

 

사용방법은 너무 너무 간단합니다. ^^

그냥 예전에 널 포인터로 0 이나 NULL을 사용하던 것을 그대로 대처하면 됩니다.

 

char* p = nullptr;

 

<List1>에서 널 포인트를 파라미터로 넘겨서 func( double* p )가 호출하게 하기 위해서는

func( nullptr );

로 호출하면 됩니다.

 



nullptr의 올바른 사용과 틀린 사용 예

 

 

올바른 사용

char* ch = nullptr; // ch에 널 포인터 대입.

sizeof( nullptr ); // 사용 할 수 있습니다. 참고로 크기는 4 입니다.

typeid( nullptr ); // 사용할 수 있습니다.

throw nullptr; // 사용할 수 있습니다.

 

 

틀린 사용

int n = nullptr; // int에는 숫자만 대입가능한데 nullptr은 클래스이므로 안됩니다.

 

Int n2 = 0

if( n2 == nullptr ); // 에러

 

if( nullptr ); // 에러

 

if( nullptr == 0 ); // 에러

 

nullptr = 0; // 에러

 

nullptr + 2; // 에러

 

 

 

nullptr 너무 간단하죠? ^^

VC++ 10에서는 예전처럼 널 포인터를 나타내기 위해서 0 이나 NULL 매크로를 사용하지 말고 꼭 nullptr을 사용하여 함수나 템플릿에서 널 포인터 추론이 올바르게 되어 C++을 더 효율적으로 사용하기 바랍니다.^^

 

 

 

짜투리 이야기...... ^^


왜 nullptr 이라고 이름을 지었을까?

nullptr을 만들 때 기존의 라이브러리들과 이름 충돌을 최대한 피하기 위해서 구글로 검색을 해보니 nullptr로 검색 결과가 나오는 것이 별로 없어서 nullptr로 했다고 합니다.

제안자 중 한 명인 Herb Sutter은 현재 Microsoft에서 근무하고 있는데 그래서인지 C++/CLI에서는 이미 nullptr 키워드를 지원하고 있습니다.

 

 

C++0x 이야기

근래에 Boost 라이브러리의 thread 라이브러리가 C++0x에 채택 되었다고 합니다. Boost에 있는 많은 라이브러리가 C++0x에 채택되고 있으므로 컴파일러에서 아직 지원하지 않는 C++0x의 기능을 먼저 사용해 보고 싶다면 꼭 Boost 라이브러리를 사용해 보기 바랍니다.

 


 

참고

http://d.hatena.ne.jp/faith_and_brave/20071002/1191322319

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf

http://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/nullptr

http://d.hatena.ne.jp/KZR/20080328/p1