2014년 2월 24일 월요일

C++11 Feature 정리 - Lambda

Code Project의 "Ten C++11 Features Every C++ Developer Should Use"를 기반으로 
C++11 feature들에 대해서 대충 정리 함.

http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer

[Lambda Expression]

- Lambda expression은 보통 a lambda function과 lambda를 지칭한다. 
- anonymous function/function object를 정의 & 사용하기 위한 간략한 표기법
- algorithms이나 logic에서 operation을 argument로 전달할때 유용함. (callback과 같이)

Syntax


: http://en.cppreference.com/w/cpp/language/lambda


[ capture ] ( params ) mutable exception attribute -> ret { body }(1)
[ capture ] ( params ) -> ret { body }(2)
[ capture ] ( params ) { body }(3)
[ capture ] { body }(4)
1) Full declaration
2) Declaration of a const lambda: the objects captured by copy cannot be modified.
3) Omitted trailing-return-type: the return type of the closure's operator() is deduced according to the following rules:
  • if the body consists of the single return statement, the return type is the type of the returned expression (after rvalue-to-lvalue, array-to-pointer, or function-to-pointer implicit conversion)
  • otherwise, the return type is void
4) Omitted parameter list: function takes no arguments, as if the parameter list was ()

Explanation

mutable-allows body to modify the parameters captured by copy, and to call their non-const member functions
exception-provides the exception specification or the noexcept clause for operator() of the closure type
attribute-provides the attribute specification for operator() of the closure type
capture-specifies which symbols visible in the scope where the function is declared will be visible inside the function body.
A list of symbols can be passed as follows:
  • [a,&b] where a is captured by value and b is captured by reference.
  • [this] captures the this pointer by value
  • [&] captures all automatic variables mentioned in the body of the lambda by reference
  • [=] captures all automatic variables mentioned in the body of the lambda by value
  • [] captures nothing
params-The list of parameters, as in named functions
ret-Return type. If not present it's implied by the function return statements ( or void if it doesn't return any value)
body-Function body

[Example]
: http://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11

기존 
#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

Lambda 사용 시
void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

사실 한곳이나 한번만 사용하는 일회성 function object인 경우 굳이 별도로 정의할 필요가 없어
사용되는 algorithms의 arguments로서 간략히 정의 하고자 하는 것이 장점. 

trailing return type 형태로 return type 명시 예
void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) -> double {
                   if (d < 0.0001) {
                     return 0;
                   }
                   else {
                     return d;
                   }
                 });
}

[Capture]
 : lambda body내에서 사용할 argument 명시

- lambda는 inline 형태의 이름 없는 function/ function object로 어디서나 사용되어야 하므로 내부 변수를 참조 할 수 없다. 

void func5(std::vector<double>& v, const double& epsilon) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [epsilon](double d) -> double {
                   if (d < epsilon) {
                     return 0;
                   }
                   else {
                     return d;
                   }
                 });
}
You can capture by both reference and value, which you can specify using = and &:
  • [&epsilon] capture by reference
  • [&, epsilon] specify that the default way of capturing is by reference and what we want to capture
  • [=, &epsilon] capture by value by default, but for epsilon use reference instead

- [] 비어 있는 empty list
- [capture list] : explicit capture
- &, = 를 사용하여 capture by reference , capture by value 지정 가능
 . [&] : implicitly capture by reference
 . [=] : implicitly capture by value
 . [&, capture list]
   : implicitly capture by reference all local variables with names not mentioned in the list. 
   : capture list에 this는 포함 될 수 있음.
 . [=, capture list] : 
   : implicitly capture by value all local variables with names not mentioned in the list.
   : capture list에 this는 포함 될 수 없음.



ret operator()(params) const { body }
(the keyword mutable was not used)
ret operator()(params) { body }
(the keyword mutable was used)

- 기본적으로 Lambda의 body는 operator()() prototype을 가지며 기본이 const이다. 
 : 만약 내부에서 captured variables를 수정하고 싶다면 mutable 속성을 지정해 주어야 한다.

void algo(vector<int>& v)
{
  int count = v.size();
  std::generate(v.begin(), v.end(),
     [count]()mutable{ return --count; }
  };
}

[Lambda and Lifetime]

lambda가 caller보다 lifetime이 길 경우 lambda에서 참조하는 arguments들의 확인이 필요함.

void setup(Menu& m)
{
  Point p1, p2, p3;
  m.add("draw triangle", [&]{m.draw(p1,p2,p3); });  // probable disater
}
위 예제에는 p1, p2, p3가 reference로 참조 되는 상태에서 lambda가 나중에 사용될 목적으로 저장 되는 상황이라 문제 발생 가능함. 이때는 reference & 에서 copy = 로 사용하는 것이 맞다.

- Lambda에서 name space를 capture할 필요는 없음.

- Lambda의 argument로 this를 사용할 수 있음. this는 call by reference로 참조되어 class instance의 member variable을 참조할 수 있음. 단 multi-threaded program에서 race condition 문제를 유발 가능.

[Call and Return]
- lambda가 argument를 받지 않는다면 생략 가능, 가장 minimal lambda expression = []{}
- lambda가 return type을 명시하지 않는다면 body를 통해 판단함. return이 있다면 return value의 type, 없다면 void

void g(double y)
{
  [&]{ f(y); }  // void
  auto z1 = [=](int x){return x+y}  // double
  auto z2 = [=,y]{ if (y) return 1; else return 2;}  // error : body is too complicated for return type deduction
  auto z3 = [y]() { return 1: 2;} // int
  auto z4 = [=,y]()->int { if (y) return 1; else return 2; }  // explicit return type
}

- lambda는 template instantiation mechanism으로 인해 동일 type의 lambda가 동시에 사용될 수 없음.

* 일부 소스는 "The C++ Programming Language - Bjarne Stroustrup"에 포함된 예제 입니다.

댓글 없음:

댓글 쓰기