簡體   English   中英

C ++ 11 / Lambda函數和函數指針

[英]C++11 / lambda functions & function pointers

好吧,也許我的問題有些人為的。

我試圖找到一種優雅的解決方案,以便每次我想修改只能通過const Getter和non const Setter訪問的對象的一部分時,減少以下樣板代碼。

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

我知道我可以有一個非常量獲取器,但是我暫時想堅持使用它。

因此,我嘗試使用lambdas,目前有以下實現:

#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)

(嗯,您還需要將func2重命名為一個地方的func )。 無論如何,通過引用傳遞函數指針實際上並沒有任何意義。 這樣做只會增加另一個間接作用而沒有任何好處,而不必要的間接作用只會浪費時間。

只是為了說明臨時信息的來源:lambda表達式的類型是每個lambda表達式的唯一類型。 如果lambda函數的捕獲為空,則可以將其轉換為函數指針。 在您的第一個代碼中,您明確地進行了此轉換,產生了一個可以綁定到引用的左值。 在其他兩種情況下,您依賴於隱式轉換,該轉換產生的收益率和rvalue不能綁定到非const引用(也就是說,您可以通過在&前面添加const來解決此問題,但額外的間接尋址是仍然毫無意義)。

您可以使用模板啟用方法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;
}

朝完全不同的方向前進,讓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) ) ;

值得慶幸的是,我認為吸氣劑/定阻劑的趨勢正在下降。

下面是使用Barmaley.exe建議的更正代碼。

我們越來越接近具有類似於C#屬性的行為,因為類似

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

可以調用非平凡的設置器(例如,它將更改對象修改日期)

完整代碼如下:

#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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM