2012-04-26 68 views
5

我真的很喜歡C#中的屬性概念,作爲一個小項目,我一直在修改C++實現它們的想法。我遇到了這個例子https://stackoverflow.com/a/5924594/245869,這看起來相當不錯,但我忍不住想起lambdas和非靜態數據成員初始化可能使得使用這個想法使用一些非常好的語法成爲可能。下面是我實現的:C++ 11;非靜態數據成員初始化可以訪問其他數據成員嗎?

#include <iostream> 
#include <functional> 

using namespace std; 


template< typename T > 
class property { 

public: 
    property(function<const T&(void)> getter, function<void(const T&)> setter) 
     : getter_(getter), 
      setter_(setter) 
    {}; 

    operator const T&() { 
     return getter_(); 
    }; 

    property<T>& operator=(const T& value) { 
     setter_(value); 
    } 

private: 
    function<const T&(void)> getter_; 
    function<void(const T&)> setter_; 

}; 


class Foobar { 

public: 
    property<int> num { 
     [&]() { return num_; }, 
     [&](const int& value) { num_ = value; } 
    }; 

private: 
    int num_; 

}; 


int main() { 
    // This version works fine... 
    int myNum; 
    property<int> num = property<int>(
     [&]() { return myNum; }, 
     [&](const int& value) { myNum = value; } 
    ); 
    num = 5; 

    cout << num << endl; // Outputs 5 
    cout << myNum << endl; // Outputs 5 again. 

    // This is what I would like to see work, if the property 
    // member of Foobar would compile... 
    // Foobar foo; 
    // foo.num = 5; 

    // cout << foo.num << endl; 

    return 0; 
} 

我可以正常使用了我的財產類[見在main()的例子]在使用屬性的數據,但與MinGW的G ++ 4.7並沒有特別在意我的嘗試成員:

\property.cpp: In lambda function: 
\property.cpp:40:7: error: invalid use of non-static data member 'Foobar::num_' 

因此,似乎我的財產執行的概念作品,但它可能是徒勞的,因爲我無法從我的lambda函數訪問其他數據成員。我不確定這個標準是如何定義我在這裏要做的,我完全是運氣不好,還是我沒有在這裏做什麼?

+3

無關:您可能希望在consructor中使用'getter_(std :: move(getter)),setter_(std :: move(setter))',以支持僅移動類型,並避免一般的無關副本。 – 2012-04-26 14:33:39

+0

問題:除了有趣之外,我相信一個簡單的'int&'會更有用。您的解決方案比簡單參考有什麼優勢? (這本身是沒用的......) – 2012-04-26 15:34:04

+0

@ R.MartinhoFernandes我完全同意。 'property <>'本身也需要支持複製/移動語義,以便可以複製/移動具有屬性的類。我希望儘可能保持最小化,直到我能夠使這個概念發揮作用。雖然謝謝! – 2012-04-26 16:49:28

回答

2

您的財產是與包含對象(實例Foobar)不同的對象(property<int>的實例)。因此,它的成員函數會通過不同的this,而不是您需要訪問num_的那個 - 所以你不能這樣做。如果lambda是在非靜態成員函數Foobar中定義的,則它們將捕獲該函數的this參數,並且將有權訪問封閉對象的成員(顯式爲this->num_)。但lambda在類中定義,其中非靜態數據成員實際上不存在。如果lambda 確實可以訪問num_,其中num_,其中Foobar的實例應該是那個?

我看到的最簡單的解決方案是爲屬性存儲指向封閉對象的指針。這樣,它可以自由訪問其非靜態成員。缺點是聲明稍微複雜一些(你必須做property<int, Foobar> num),你需要通過傳遞this指針來初始化屬性。所以你不能在類中做它,它必須在構造函數的初始化列表中,因此否定了C++ 11數據成員初始化的優點。

在這一點上,this將可用於lambdas無論如何捕獲(通過值,而不是引用!),所以如果你的屬性的初始化移動Foobar的構造函數(s ):

Foobar::Foobar(): 
    num { 
     [this]() { return this->num_; }, 
     [this](const int& value) { this->num_ = value; } 
    } 
{ 
} 

有誰知道是否this,它被傳遞到任何構造恰好被調用,可在類定義的非靜態成員初始化?我懷疑它不是,但如果是這樣的話,同樣的構造可以在班級定義中起作用。

+0

我只是想補充一個更常見的問題,這是一個特殊情況,即從一個對象訪問封閉對象,而不在內部對象中存儲一個指向外部對象的指針,在C++中一直很棘手。它可以做到,但它看起來不太好,我相信指針魔術涉及打破嚴格的別名規則... – cvoinescu 2012-04-26 15:18:52

+0

我問了一個小時後才發現,使捕獲子句[this]修正了這個問題對數據成員的訪問,我不確定爲什麼這是必要的,但你的答案爲我清楚了。謝謝!事實證明,你不需要把它移到構造函數的初始化列表中。使用代碼修改捕獲條款,因爲我擁有完美的代碼。 – 2012-04-26 16:46:39

+0

雖然還有一個迫在眉睫的問題,但這與我原來的問題並不完全相關。由於你解釋的原因,lambda表達式不能訪問我的'Foobar'類的私有數據成員。我還沒弄清楚如何從'Foobar'正確地朋友'財產'來完成這項工作。將數據成員設置爲「public」編譯並運行完美,所以我認爲好友是解決方案。 – 2012-04-26 16:48:27