'C++11'에 해당되는 글 17건

  1. 2013.02.18 [C++11 Overview] Unicode Support
  2. 2013.02.18 [C++11 Overview] enum
  3. 2013.02.18 [C++11 Overview] nullptr
  4. 2013.02.18 [C++11 Overview] Range-Based for Loops
  5. 2013.02.18 [C++11 Overview] Counting Words Across Files
  6. 2012.02.01 C++11, Lambda Expressions #2
  7. 2012.01.30 C++11, Lambda Expressions #1 2

[C++11 Overview] Unicode Support

Book Review 2013. 2. 18. 16:00

C++11 에서는 기존의 wchar_t 타입도 그대로 지원되기는 하지만,


새롭게 char16_t , char32_t 타입이 등장했습니다.


wchar_t 같은 경우 운영체제의 locale 에 따라 크기가 달라지는 문제가 있기 때문에,


C++11 을 사용 가능하다면 새로운 타입의 사용에 익숙해지는 것이 좋겠습니다.


현재 VS2012, GCC 4.8 beta 모두 char16_t , char32_t 에 대한 타입 지원은 되고 있지만,


unicode literal 에 대한 prefix ( u8'...' , u'...' , U'...' ) 는 gcc 만 지원되고 있는 상황입니다.

'Book Review' 카테고리의 다른 글

[C++11 Overview] Uniform Initialization Syntax  (0) 2013.02.22
[C++11 Overview] Raw String Literals  (0) 2013.02.22
[C++11 Overview] enum  (0) 2013.02.18
[C++11 Overview] nullptr  (0) 2013.02.18
[C++11 Overview] Range-Based for Loops  (0) 2013.02.18
:

[C++11 Overview] enum

Book Review 2013. 2. 18. 15:22

enum Classes


C++11 에서는 enum 타입에 대한 지원이 강력해 졌습니다.


enum 타입이 다른 타입으로 암시적으로 변환되는 것을 차단하여


enum 값이 의도치 않게 사용되는 것을 사전에 막아버립니다.

(명시적으로 캐스팅하는 것은 허용되고 있습니다.)



Forward-Declaring


C++98 에서는 enum 타입의 전방 선언이 불가능 했지만,


C++11 에 오면서 가능해졌습니다.


'Book Review' 카테고리의 다른 글

[C++11 Overview] Raw String Literals  (0) 2013.02.22
[C++11 Overview] Unicode Support  (0) 2013.02.18
[C++11 Overview] nullptr  (0) 2013.02.18
[C++11 Overview] Range-Based for Loops  (0) 2013.02.18
[C++11 Overview] Counting Words Across Files  (0) 2013.02.18
:

[C++11 Overview] nullptr

Book Review 2013. 2. 18. 14:56

C++ 세계에 nullptr 이 등장했습니다.


literal value 0 은 


int a = 0;

int* p = 0;


이렇게 value 와 pointer 모두에 대입이 가능했습니다.


하지만 새로 등장한 nullptr 은


int* p = nullptr;

int a = nullptr; // error


value 로의 대입에 대해서는 에러를 내게 됩니다.

( reinterpret_cast<int>(nullptr) 같이 캐스팅을 통해 대입은 가능하지만.. )


책에서 주목할 만한 부분은 템플릿 코드의 인자 추론과 관련되는 부분인데,



이 코드에서 f(0) 에서 0 은 묵시적으로 널포인트 처리되지만


logAndCall 에서 0 은 int 형 으로 추론되고 f(int) 함수를 찾게 되기 때문에


에러를 내게 됩니다.


:

[C++11 Overview] Range-Based for Loops

Book Review 2013. 2. 18. 13:33
1. 배열

2. std 컨테이너

3. Initializer list

4. User Defined Type


Visual Studio 2012 , GCC 4.8 Release Beta 모두 Range-Based Loop 는 잘 지원해 주고 있습니다.

다만, Visual Studio 2012 의 경우 Initializer list 를 아직 미지원하기 때문에 3 번의 경우 에러가 납니다.



그리고 책에서는 unordered_multiset 과 shared_ptr 에 대해 아직 소개되지 않고 있다고 되어있지만

위 샘플 코드는 VS2012, GCC4.8 모두에서 잘 컴파일 됩니다.


'Book Review' 카테고리의 다른 글

[C++11 Overview] enum  (0) 2013.02.18
[C++11 Overview] nullptr  (0) 2013.02.18
[C++11 Overview] Counting Words Across Files  (0) 2013.02.18
이야기로 아주 쉽게 배우는 대수학  (0) 2012.02.22
제4판 Windows 시스템프로그래밍  (8) 2012.01.30
:

[C++11 Overview] Counting Words Across Files

Book Review 2013. 2. 18. 10:41

From : Book [ Overview of The New C++ (C++11) ]


현재 Visual Studio 는 2012 , GCC 는 4.8 베타 릴리즈 까지 나와 있는데,


둘 모두 아직까지는 C++11 을 완벽하게 지원하고 있지는 못 합니다.


그래서 C++11 의 기능들을 테스트 해보기 위해서는 둘을 같이 사용해서 테스트 해보는게 좋겠습니다.


using = ; 이나 initializer list 같은 문법 사항들은 GCC 쪽이,


std::regex 나 std::future 같은 표준 라이브러리 부분은 visual studio 가 더 지원을 해주는 느낌입니다.



'Book Review' 카테고리의 다른 글

[C++11 Overview] enum  (0) 2013.02.18
[C++11 Overview] nullptr  (0) 2013.02.18
[C++11 Overview] Range-Based for Loops  (0) 2013.02.18
이야기로 아주 쉽게 배우는 대수학  (0) 2012.02.22
제4판 Windows 시스템프로그래밍  (8) 2012.01.30
:

C++11, Lambda Expressions #2

Short Articles 2012. 2. 1. 18:52
앞 글에 이어 우선 lambda-declarator 부분을 잠시 보고, capture 와 관련된 부분들을 다시 살펴 보겠습니다.

+ lambda-declarator
lambda-declarator 는

(파라미터 선언 절) mutable exception-specification attribute-specification trailing-return-type


의 형식으로 작성되고,
이 중에서 (파라미터 선언 절) 외의 부분들은 필요에 따라 적어주면 되는 부분들입니다.
lambda-declarator 에 포함된 사항들은 C++ lambda 의 새롭게 추가된 사항이 아닌 기존 C++ 에 이미 정의된 사항들 이므로 자세한 내용은 생략 하겠습니다.

간단한 예시 코드,
이 람다식이 포함하는 closure object 는 ecode 변수를 copy 로 capture 합니다.
mutable 키워드는 capture 를 compound-statement 내에서 수정 가능함을 나타내고,
뒤따라오는 throw(int) 는 int type 의 예외를 던진다는 것을 알립니다. ->int 는 compound-statement 의
리턴 타입이 int 형임을 지정합니다.

[=](char const *s) throw(int) ->int ...  와 같이 mutable 키워드를 지정하지 않는 경우
closure 에서 public 으로 선언(정의)되는 function call operator 는 기본 const 입니다.

다시 capture 관련 이야기로 돌아가서...

local-lambda expression
block scope 바로 아래 범위에 닿아 있는 람다 표현식을 local lambda expression 이라고 합니다.
local lambda expression 의 영향 범위는 자신의 한겹 위쪽 블럭과 아래쪽으로 포함되는 영역 입니다.
(함수와 함수의 파라미터를 포함해서..)
이 유효 범위 사이에는 또다른 람다 표현식이 끼어들 수도 있습니다.


위 코드에서 람다 표현식의 범위는 PrintPoint 함수의 블럭과 표현식이 포함하고 있는 출력 코드 부분입니다.
이 경우  name 은 local lambda expression 의 유효 범위 밖에 있기 때문에 에러가 납니다.
이 때에는 명시적으로 this 를 capture 하거나 PrintPoint 의 지역 변수로 const char* 하나를 선언해서
name 의 값을 받은 후 이것을 명시적으로 capture 하는 식으로 에러를 피할 수 있습니다.

lambda 표현식의 capture-list 에 대한 name look up 은 일반적인 unqualified name look up
(:: , std:: , boost::  등으로 특정 짓지 않은 entitiy 에 대한 name look up) 에 따릅니다. 
각각의 룩업은 local lambda expression 의 유효 범위 내에서 automatic storage duration 으로
선언된 변수를 찾습니다.

automatic storage duration :
자동으로 생명주기가 관리되는 것들을 말한다. variable with automatic storage duration 이라 하면,
C++ 에서는 주로 stack 에 생성되는 지역 변수등을 일컫는다.

 
ODR & lambda-expression

(참고. cl.exe 는 아직 implicitly capture 를 제대로 지원하지 못 하고 있습니다. )

람다 표현식이 capture-default 를 포함하고 표현식의 odr 사용이 this 나 automatic storage duration
변수 그리고 odr이 사용된 entity 가 명시적으로 캡처(explicitly capture)되지 않았다면,
odr이 사용된 entity 는 임시적으로 캡처(implicitly capture) 되었다고 합니다.

다음은 표준 문서에 나오는 implicitly capture 에 대한 예제 코드 입니다.
코드 작성과 테스트는 eclipse with g++4.6.2 로 했습니다.
이 코드를 컴파일하면 다음과 같은 에러를 보게 됩니다.

../src/hello.cpp: In lambda function:

../src/hello.cpp:38:18: error: 'j' is not captured

../src/hello.cpp: In lambda function:

../src/hello.cpp:39:14: error: 'n' is not captured

../src/hello.cpp:43:11: error: 'i' is not captured

../src/hello.cpp: In lambda function:

../src/hello.cpp:45:5: error: unable to deduce 'auto' from '<expression error>'


N 과 M 은 m1 에 의해서 implicitly capture 되었기 때문에 괜찮지만,
j 는 m3 에 의해서 캡처 되지 않았고 n 은 m4 에 의해서는 implicitly capture 되지만
m4 를 둘러싸고 있는 m3 가 n 을 캡처 하지 않았기 때문에 에러입니다.
그리고 i 는 유효범위 밖에 있으므로 역시 에러가 나고 있습니다.

다음과 같이 명시적으로 n, j 를 캡처하고 x += i; 를 주석 처리 하면
에러없이 컴파일 되는 것을 확인할 수 있습니다.
 
기본 인자로 lambda-expression 이 올 때, 어떤 entity 도 명시적이든 암시적이든 캡처할 수 없습니다.
이 부분은 개인적으로 그다지 중요한 부분은 아니라고 생각하는데,
아무튼 람다 표현식이 기본인자로 쓰일 경우 

void foo(int = ( []{ return 0;}() ));

처럼 상수식이 반환되는 경우는 ok 지만 어떤 것이든 캡처를 하는 경우는 에러입니다.
별다른 내용은 없습니다.

entity - captured by copy
entity 가 implicitly capture 되었고 capture default 가 '=' 이거나
entity 가 explicitly capture 되었고 '&' 를 포함하지 않으면,
이 entity 는 copy 로 캡처 되었다고 합니다.

by copy 로 capture 캡처된 entity 는 closure 의 non-static unnamed 데이터 멤버로 선언됩니다.
이 때, 선언되는 순서에 정해진 규칙은 없습니다. entity 가 객체나 그 밖의 타입에 대한 참조가
아닐 경우 이렇게 되며, int &p = value; 에서의 p 같은 참조 변수는 captured by copy 되지 않습니다.

entity - captured by reference 
명시적으로 또는 암시적으로 캡처되지만 copy 로 캡처되지 않는 entity 는 reference 로 캡처됩니다.

nested lambda expressions 에서 한 표현식을 다른 표현식이 바로 둘러 싸는 경우
표현식 m1이 m2 를 둘러 싸고 있다고 할 때,
m1 이 by copy 로 캡처한 entity 는 m1 의 closure 타입의 non-static data member 에 대응되는 것으로
m2 에 캡처됩니다. 그리고 m1 이 by reference 로 캡처한 entity 는 m1 이 캡처한 것과 동일한 entity 로
m2 가 캡처합니다.
이 코드의 결과 값은 다음과 같습니다.

123234



 
람다 표현식에 대한 글은 여기서 이만 줄이겠습니다.
표준 문서에 몇 가지 추가적인 사항에 대한 설명이 나오므로 더 자세한 사항을 원하시는 분은
표준 명세를 찾아보시기 바랍니다.
 
:

C++11, Lambda Expressions #1

Short Articles 2012. 1. 30. 18:41
이미 C++11 의 람다 표현식에 대한 내용이 여러 블로그들에 많이 올라와 있지만
다시 정리해본다는 생각으로 C++11 표준 문서와 MSDN 에 올라온 내용을 바탕으로 정리해보겠습니다.
이 코드는 C++11 표준 문서 5.1.2 처음에 나오는 샘플 코드를 약간 수정한 것입니다.
위 코드에서는 배열 정렬 확인을 위한 출력 코드 삽입하면서 람다 표현식이 한번 더 사용되었습니다.
람다 표현식을 쓰지 않고 기존 방식으로 Functor 를 사용한다면
아마도 다음과 같은 식의 코드가 작성 될 것입니다.

이 두 코드를 일단 코드의 재활용성 측면에서 보자면..
절대값 비교를 위한 Functor 가 여러 곳에서 자주 사용되는 경우에는
매번 람다 표현식을 통해 평가식을 작성하는 것 보다는 Functor 를 한번 작성해 두고
이 것을 계속 재활용 하는 편이 훨씬 이득이 클 것입니다.
하지만, 단 한 번 사용될 평가식을 위해서 Functor 를 정의 하는 것은 비효율적 이겠지요.
더불어 Functor 의 이름 짓기도 다소 신경이 쓰이는 부분입니다.

언어학적으로도 그렇지만 프로그래밍에서도 적절한 메타포 의 작성은 상당히 중요합니다.
만약 다음과 같이 구조체의 이름을 정의한다면
이것을 보게 될 누군가는 작성자를 정말 썰어버리고 싶어질지도 모릅니다.


이런 어의없는 경우가 아니더라도
본인은 정말 기가막히게 메타포를 연결했다고 생각하는 것이
그것을 보는 상대방은 전혀 납득이 가지 않을 수도 있습니다.

지속적으로 재활용할 코드(평가식)도 아니고
어떤 이름을 붙여야할지 애매한 Functor 를 작성해야 할 때,
이럴 땐 오히려 그냥 코드의 내용을 풀어 보여주는 편이 나을 수 있습니다.
이 때 유용하게 쓸 수 있는 것이 람다 표현식이고
람다를 또 다른 말로는 '이름 없는 함수', '익명 함수' 라고 합니다.

C++11 표준 문서 5.1.2 Lambda Expressions 첫 부분을 보면 바로 다음과 같이 설명하고 있습니다.
Lambda Expressions provide a concise way to create simple function objects.
이 문장과 함께 예로 나온 코드가 바로 앞서 본 절대값 정렬을 하는 람다 표현식입니다.

이 외에도 람다 표현식의 주요 용법 중 하나로 Lazy Evolution (또는 Late Evolution)에 대한 부분도 나오는데
이건 나중에 살펴보기로 하고.. 일단 람다 표현식의 문법적인 부분을 살펴 보겠습니다.

람다 표현식은 가장 단순하게 적으면 다음과 같이 적을 수 있습니다.

[ ] ( ) { }




여기서 [] 부분을 lambda-introducer, () 부분을 lambda-declarator, {} 부분을 compound-statement 라고 합니다.
(lambda-declarator 는 생략 가능합니다.)

+ lambda-introducer
lambda-introducer 는 lambda-capture 를 포함 할 수 있습니다.
[=, a, &b, c] 이런식으로요.
lambda-capture 는 capture-default 라는 놈과 capture-list 라는 놈이 있는데
하나만 사용할 수도 있고, 둘 다 사용할 수도 있습니다.
단, 둘 다 사용할 경우에는 capture-default 가 먼저와야 합니다.

그럼 lambda-capture 부터 살펴 보겠습니다.
lambda-default 는 =, & 이렇게 딱 두개가 존재하는데
capture 에 대한 기본 속성을 지정해 주는 역할을 합니다.
'&' 는 by-reference capture 라고 하고 
뒤이어 오는 capture-list 는 &로 시작하는 identifier 는 올 수 없습니다.
'=' 는 by-value capture 라고 하고
뒤이어 오는 capture-list 는 this 와 일반적인 identifier 는 올 수 없고, 
&로 시작하는 identifier 만 올 수 있습니다.


몇 가지 예를 들어 보겠습니다.

(1) by-value capture
이 람다식은 lambda-declarator 는 생략 되었고, 뒷 부분에 오는 () 는 function call operator 입니다.
아무튼 이 코드는 잘 동작하고 sum 의 값은 2로 계산되어 출력될 것입니다.

하지만 람다식 내에서 a,b,c 에 대해서 전위 연산을 하려 할 경우
다음과 같은 에러 메시지를 보게 되는데..

error C3491: 'a': 변경 불가능한 람다에서 값 방식 캡처를 수정할 수 없습니다.

error C3491: 'b': 변경 불가능한 람다에서 값 방식 캡처를 수정할 수 없습니다.

error C3491: 'c': 변경 불가능한 람다에서 값 방식 캡처를 수정할 수 없습니다.


이것은 위의 람다식에서 closure 의 inline function call operator 가 const 로 지정되기 때문입니다.
필요한 경우 mutable 키워드를 붙여주면 람다식 내에서 a,b,c 에 대한 값 변경 가능해집니다.


아무튼 여기서 중요한 것은 a,b,c 는 value-capture 하였기 때문에
람다식 내에서 a, b, c 값을 수정 하더라도 a, b, c 의 값이 변경 되지 않는다는 것입니다.
수정값은 람다식 내에서만 유효합니다.

여기서 잠깐..
클로저(closure)의 개념에 대해 생소하시다면 잠시 클릭 -> 클로저 

람다 표현식이 closure 를 포함 하기 때문에 capture 시점의 상태를 저장할 수 있고
이를 통해서 앞서 잠시 언급했던 lazy evolution 이 가능해 집니다.
일단 상태만 저장하고 있다가 프로그램이 한가해지는 (적절한) 시점에서 값을 평가(계산)해서
프로그램의 부하가 분산되도록 유도하는 것이죠.

예를 들면 다음과 같이 배열의 초기값을 기억하고 있다가 나중에 계산이 필요한 시점에서
사용하는 식으로요.

12번 줄에서 float 배열 fa 의 값들은 value-capture 되어
std::function 을 통해서함수 바인딩되어 lst 에 저장됩니다.
그리고 중간에 fa 의 값이 마구 변경이 되더라도나중에 lst 에 저장 되었던
람다 표현식의 closure 의 function call operator 를 호출하게 되면
capture 당시 저장했던 값 그대로 이용하여 연산식을 수행하고 결과값을 출력해 줍니다. 

(2) by-reference capture
reference capture 를 하게 되면 value capture 와는 다르게 람다 표현식 내에서의 수정이
외부에도 동일하게 적용 됩니다. 일반적인 C++ 함수에서 파라미터를 reference 로 받는 것 같이요.

위에서 예를 들었던 코드를 by-reference capture 로 바꿔서
다음과 같이 사용하면..
이 람다 표현식에서는 fa 의 요소들을 reference 로 capture 하였기 때문에
출력값을 확인하면 모두 0.912945 가 나옵니다. 

만약 sin(f*2.f + 20.f) 에서 고정값인 20.f 대신에 외부에서 계속 변경되는 변수의 값을 적용되도록 하되
f 는 앞서 value-capture 에서 본 것처럼 초기값을 그대로 사용하고 싶다면 어떻게 하면 될까요?
default capture 로 by-reference capture 를 지정하고 capture-list 에 identifier f 를 적어주면 되겠죠.
계산식은 값 확인이 편하도록 단순하게 고쳤습니다.

결과값은 다음과 같이 나올 것입니다.

0.1
2.1
0.8
10
0.02

20.1
22.1
20.8
30
20.02 


배열 fa 의 값은 capture 당시값 그대로를 유지하지만 p 의 값은 변경한대로 전달이 되었습니다.

짧게 쓰려고 했는데 글이 생각보다 길어지네요.
다음 글에서 이어서 적겠습니다.

 

: