2012-04-26 28 views
5

Tôi thực sự thích ý tưởng về các thuộc tính trong C#, và như là một dự án nhỏ bên, tôi đã tinkering với ý tưởng triển khai chúng trong C++. Tôi chạy vào ví dụ này https://stackoverflow.com/a/5924594/245869 mà có vẻ khá tốt đẹp, nhưng tôi không thể không nghĩ rằng lambdas và khởi tạo thành viên dữ liệu không tĩnh có thể làm cho nó có thể sử dụng một số cú pháp rất tốt đẹp với ý tưởng này. Dưới đây là thực hiện của tôi:C++ 11; Có thể khởi tạo thành viên dữ liệu không tĩnh truy cập các thành viên dữ liệu khác không?

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

tôi có thể sử dụng lớp tài sản của tôi bình thường [xem ví dụ trong main()], nhưng MinGW với g ++ 4.7 không đặc biệt quan tâm đến nỗ lực của tôi lúc sử dụng tài sản như một dữ liệu thành viên:

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

Vì vậy, có vẻ như vô ích vì tôi không thể truy cập các thành viên dữ liệu khác từ các hàm lambda của tôi. Tôi không chắc làm thế nào tiêu chuẩn xác định những gì tôi đang cố gắng để làm ở đây, tôi hoàn toàn không may mắn, hoặc tôi chỉ không làm điều gì đó ngay tại đây?

+3

Không liên quan: Bạn có thể muốn 'getter_ (std :: move (getter)), setter_ (std :: move (setter))' trong consructor, để hỗ trợ các kiểu di chuyển, và tránh các bản sao không liên quan nói chung. –

+0

Câu hỏi: ngoài việc thú vị, tôi tin rằng một 'int &' đơn giản sẽ hữu ích hơn. Lợi thế của giải pháp của bạn trên một tham chiếu đơn giản là gì? (Mà chính nó là vô ích ...) –

+0

@ R.MartinhoFernandes Tôi hoàn toàn đồng ý. Bản thân 'property <>' cũng cần hỗ trợ các ngữ nghĩa sao chép/di chuyển để các lớp với các thuộc tính có thể được sao chép/di chuyển. Tôi muốn tiếp tục triển khai càng ít càng tốt cho đến khi tôi có thể làm cho khái niệm này hoạt động. Cảm ơn mặc dù! –

Trả lời

2

Thuộc tính của bạn là một đối tượng khác (ví dụ: property<int>) từ đối tượng chứa (ví dụ: Foobar). Như vậy, các chức năng thành viên của nó được chuyển qua một số khác nhau this, không phải là chức năng bạn cần truy cập num_ - vì vậy bạn không thể thực hiện theo cách đó. Nếu lambdas được định nghĩa trong một hàm thành viên không tĩnh của Foobar, chúng sẽ bắt được đối số this của hàm đó và sẽ có quyền truy cập vào các thành viên của đối tượng kèm theo (rõ ràng là this->num_). Nhưng lambdas được định nghĩa trong lớp, nơi các thành viên dữ liệu không tĩnh thực sự không tồn tại. Nếu lambdas đã làm, hãy truy cập num_, trong đó num_, trong đó phiên bản Foobar, có phải là điều đó không?

Giải pháp dễ nhất mà tôi thấy là để thuộc tính lưu trữ con trỏ vào đối tượng bao quanh. Bằng cách đó, nó có thể tự do truy cập các thành viên không tĩnh của nó. Nhược điểm là việc khai báo hơi phức tạp hơn một chút (bạn phải làm property<int, Foobar> num) và bạn cần phải khởi tạo thuộc tính bằng cách chuyển con trỏ this. Vì vậy, bạn sẽ không thể làm điều đó trong lớp, nó sẽ phải ở trong danh sách khởi tạo của hàm dựng, do đó phủ nhận lợi thế của việc khởi tạo thành viên dữ liệu của C++ 11.

Tại thời điểm đó, this sẽ có sẵn để lambdas chụp bất kỳ giá trị nào (theo giá trị, không phải bằng tham chiếu!) Để mã của bạn thực sự hoạt động với những thay đổi tối thiểu, nếu bạn di chuyển khởi tạo thuộc tính sang hàm tạo của Foobar):

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

có ai biết liệu this, như truyền cho bất cứ điều gì xảy ra constructor được gọi, có sẵn để khởi tạo thành viên không tĩnh trong định nghĩa lớp học? Tôi nghi ngờ nó không phải là, nhưng nếu nó được, cùng một công trình sẽ làm việc bên trong định nghĩa lớp.

+0

Tôi chỉ muốn thêm rằng một vấn đề tổng quát hơn, đây là trường hợp cụ thể, cụ thể là truy cập từ một đối tượng đến đối tượng kèm theo, mà không lưu trữ một con trỏ tới đối tượng bên ngoài trong đối tượng bên trong. Nó có thể được thực hiện, nhưng nó trông không đẹp và tôi chắc chắn rằng phép thuật con trỏ liên quan phá vỡ các quy tắc bí mật nghiêm ngặt ... – cvoinescu

+0

Tôi đã tìm hiểu khoảng một giờ sau khi đặt câu hỏi này khiến mệnh đề chụp [sửa] này truy cập vào các thành viên dữ liệu, tôi đã không chắc chắn tại sao nó là cần thiết, nhưng câu trả lời của bạn xóa nó lên cho tôi. Cảm ơn! Hóa ra bạn không cần phải di chuyển nó vào danh sách khởi tạo của hàm tạo. Thay đổi mệnh đề chụp với mã tại chỗ như tôi đã có nó hoạt động hoàn hảo. –

+0

Có một vấn đề hiện ra lờ mờ, tuy nhiên, điều này không liên quan gì đến câu hỏi ban đầu của tôi. Vì những lý do bạn giải thích, lambdas không thể truy cập vào các thành viên dữ liệu cá nhân của lớp 'Foobar' của tôi. Tôi đã không hoàn toàn tìm ra làm thế nào để đúng người bạn 'tài sản ' từ 'Foobar' để thực hiện công việc này. Thiết lập các thành viên dữ liệu để biên dịch 'public' và chạy hoàn hảo, vì vậy tôi nghĩ rằng kết bạn là giải pháp. –

Các vấn đề liên quan