2013-12-16 2 views
1

좋아, 아마도 내 문제는 조금 contrieved입니다.C++ 11/lambda 함수 및 함수 포인터

const Getter 및 non const Setter를 통해서만 액세스 할 수있는 객체의 일부를 수정하고 싶을 때마다 다음 상용구 코드를 줄이기 위해 우아한 솔루션을 찾으려고합니다.

Content c = container.GetContent(); 
c.SetX(3); 
container.SetContent(c); 

나는 const가 아닌 게터를 가질 수는 있지만 시간이 지날수록 좋습니다.

그래서, 람다를 사용하려하고 나는 현재 다음과 같은 구현이 있습니다

#include <iostream> 

class Content 
{ 
public: 
    Content(int x) :mX(x) {} 
    const int GetX() const 
    { 
     return mX; 
    } 
    void SetX(const int &x) 
    { 
     mX = x; 
    } 
private: 
    int mX; 
}; 


//for clarity ContentFunctionChanger is a typedef for any function of type : void f(Content &) 
typedef void (*ContentFunctionChanger)(Content &); 


class Container 
{ 
public: 
    Container(const Content &c) :mContent(c) {} 
    const Content & GetContent() const 
    { 
     return mContent; 
    } 
    void SetContent(const Content &c) 
    { 
     mContent = c; 
    } 

    void ChangeContent(ContentFunctionChanger &function) 
    { 
     (*function)(mContent); 
    } 

private: 
    Content mContent; 
}; 


int main() 
{ 
    Content content(1); 
    Container container(content); 

    std::cout << "x=" << container.GetContent().GetX() << std::endl; 

    { 
     //Classic method using Get() then Set() 
     Content c = container.GetContent(); 
     c.SetX(3); 
     container.SetContent(c); 
     std::cout << "x=" << container.GetContent().GetX() << std::endl; 
    } 

    { 
     //Method 1 : with a named lambda function whose type is written at the declaration 
     //It works, but it is not concise 
     ContentFunctionChanger func = [] (Content & c) { c.SetX(5); }; 
     container.ChangeContent(func); 
     std::cout << "x=" << container.GetContent().GetX() << std::endl; 
    } 
    /* 
    { 
     //Method 2 : with a named lambda function whose type is not written (using auto) 
     //It will not compile... 
     auto func = [] (Content & c) { c.SetX(7); }; 
     container.ChangeContent(func); 
     std::cout << "x=" << container.GetContent().GetX() << std::endl; 
    } 

    { 
     //Method 3: with an anonmymous lambda. 
     //Concise enough, but it does not compile either... 
     container.ChangeContent([] (Content & c) { c.SetX(9); }); 
     std::cout << "x=" << container.GetContent().GetX() << std::endl; 
    } 
    */ 

    return 0; 
} 

내 문제는 2, 3 방법은보다 간결 있다는 것입니다,하지만 그들은 컴파일되지 않습니다. 컴파일 할 수있는 희망이 있는지 궁금합니다.

아무도 도와 줄 수 있습니까? 이 선언에서 &을 제거하고 작동합니다 :

void ChangeContent(ContentFunctionChanger &function) 

(물론, 당신은 또한 funcfunc2의 이름을 변경해야합니다

답변

2

당신은 방법 3을 사용하려면 템플릿을 사용할 수 있습니다 :

template<typename F> 
void ChangeContent(F function) 
{ 
    function(mContent); 
} 

(예를 들어, 펑) 호출 아무것도 통과 할 수있다.

또 다른 (C++ 03) 접근 방식은 Set 방법에 대해 유창하게 인터페이스를 구현하는 것입니다 : 다음과 같이

// kind of a functional set — if we want Set to constant, we need to return a new object 
Content SetX(const int &x) const 
{ 
    Content ret = *this; 
    ret.mX = x; 
    return ret; 
} 

그리고 그것을 사용 : 완전히 다른 방향으로가는

{ 
    //Fluent interface 
    container.SetContent(container.GetContent().SetX(111)); 
    std::cout << "x=" << container.GetContent().GetX() << std::endl; 
} 
+0

@woolstar 로컬 객체는 참조로 반환되지 않습니다. 이 경우'this'는 const이므로'* this'를 반환하도록 수정할 수는 없습니다. –

+0

함수 포인터의 복잡한 구문으로 머리를 긁적 거리지 않도록 템플릿을 사용하여 솔루션을 좋아합니다. –

3

귀하의 문제는 당신이 참조로 임시 개체를 전달하려고한다는 것입니다 한 곳에서). 어쨌든 함수 포인터를 참조로 전달하는 것은 실제로 의미가 없습니다. 이렇게하면 아무런 혜택도없이 다른 간접 광고가 추가되고 불필요한 내향 발신은 단지 시간을 소비하는 경향이 있습니다.

람다 식의 형식은 각 람다 식에 대해 고유 한 형식입니다. 람다 함수에 빈 캡처가 있으면 함수 포인터로 변환 할 수 있습니다. 첫 번째 코드에서이 변환을 명시 적으로 수행하여 참조에 바인딩 될 수있는 lvalue를 생성했습니다. 다른 두 가지 경우에는 const 참조에 바인딩 할 수없는 yield 및 rvalue가있는 암시 적 변환에 의존합니다 (즉 const& 앞에 추가하여 문제를 해결할 수 있었지만 추가 간접 참조는 여전히 무의미 함).

+0

감사합니다. 컴파일러 출력을 더 잘 읽었어야합니다! –

1

을 가지고 SetX는 개체에 대한 참조를 반환하고 인수를 연결할 수 있습니다.

Content & SetX(const int &x) 
    { 
     mX = x; 
     return * this ; 
    } 

... 

container.SetContent(container.GetContent().setX(3)) ; 

그러나이 특별한 경우에 container.GetContent()const을 반환하므로 setX를 호출 할 수도 없으므로 수정하기 위해 새 객체를 만들어야하는 경우 GetContent()을 호출하는 이유는 무엇입니까?

다른 사람들이 setX의 동작을 수정하여 새 개체를 반환하는 반면, 나는 그 것이 동사와 맞지 않는다고 생각합니다. 내가 set* 개체를 수정하고, 새로운 하나를 반환하지 않을 것으로 기대합니다. 그래서 여기에 setX의 의미를 보존하고 getter에서 const 값을 처리하는 문제를 해결하는 방법이 있습니다. 당신이 Content에서 일을 복사하지 않은 단순한 경우

, 단지 수 있도록 한 새로운 :

을 :
container.SetContent(Content(3)) ; 

또는 임시 객체에 던져, 어떤 가치 상태가있는 곳에 더 복잡한에

container.SetContent(Content(container.getContent()).setX(3)) ; 

고맙게도 getters/setters의 추세가 쇠퇴하고 있다고 생각합니다.

+0

나쁘지 않은 생각입니다. –

+0

...하지만 (woolstar가 그의 코멘트에서 설명했듯이) 컴파일되지 않습니다. 하지만 나는 원래의 개념 인 –

+0

이 getContent()의'const'를 해결했다고 생각한다. 나는 setX가 새로운 객체를 반환하는 것을 좋아하지 않는다. – woolstar

0

다음은 Barmaley.exe 제안을 사용하여 수정 된 코드입니다.

우리가 아닌 사소한 세터 (예를 들어 그 뜻이 객체 수정 날짜 변경)를 호출 할 수 있습니다

container.ChangeContent([] (Content & c) { c.SetX(9); }); 

같은 호출을 위해서 (때문에), C#을 속성을 닮은 행동을 필요로 가까워지고있다

전체 아래 코드 :

#include <iostream> 

class Content 
{ 
public: 
    Content(int x) :mX(x) {} 
    const int GetX() const 
    { 
     return mX; 
    } 
    void SetX(const int &x) 
    { 
     mX = x; 
    } 
private: 
    int mX; 
}; 

class Container 
{ 
public: 
    Container(const Content &c) :mContent(c), mWasUpdated(false) {} 
    const Content & GetContent() const 
    { 
     return mContent; 
    } 
    void SetContent(const Content &c) 
    { 
     mContent = c; 
     mWasUpdated = true; //dummy example of a non trivial setter 
    } 
    bool WasUpdated() const 
    { 
     return mWasUpdated; 
    } 

    //FnContentChanger can be a function, a functor, any callable that modifies Content... 
    //We are getting closer to having a behaviour that resemble C# properties 
    template<typename FnContentChanger> 
    void ChangeContent(FnContentChanger function) 
    { 
     Content c = GetContent(); 
     function(c); 
     SetContent(c); 
    } 

private: 
    bool mWasUpdated; 
    Content mContent; 
}; 


int main() 
{ 
    { 
     //Classic method using Get() then Set() 
     Content content(1); 
     Container container(content); 

     std::cout << "x=" << container.GetContent().GetX() << " wasUpdated=" << container.WasUpdated() << std::endl; 
     Content c = container.GetContent(); 
     c.SetX(3); 
     container.SetContent(c); 
     std::cout << "x=" << container.GetContent().GetX() << " wasUpdated=" << container.WasUpdated() << std::endl; 
    } 

    { 
     //Method 2: with an anonmymous lambda. 
     Content content(1); 
     Container container(content); 

     std::cout << "x=" << container.GetContent().GetX() << " wasUpdated=" << container.WasUpdated() << std::endl; 
     container.ChangeContent([] (Content & c) { c.SetX(9); }); 
     std::cout << "x=" << container.GetContent().GetX() << " wasUpdated=" << container.WasUpdated() << std::endl;  
    } 
    return 0; 
}