2016-04-07 22 views
9

Tôi muốn lặp qua mảng nổi được phân bổ trước với vùng chứa tùy chỉnh không sở hữu dữ liệu nhưng hoạt động trên phân đoạn của nó. Ví dụ, đặt tên cho các lớp container LinhaSobre:Quy tắc trả về tham chiếu đối tượng giả trong C++

std::unique_ptr<float[]> data(new float[720]); 
... 
//creates container to iterate 26 floats starting from from data[12] 
    LinhaSobre cont(data.get()+12, 26); 
//sets those elements to 1.5 
    for(size_t i = 0; i < cont.size(); i++) 
     cont[i] = 1.5f; 

Dưới đây là một thực thể của operator[]:

//... 
//LinhaSobre has a member mem0 which is initialized 
//as a pointer to where the interval starts 
float & LinhaSobre::operator[] (size_t i) 
{ 
    return *(mem0+i); 
} 

Chú ý rằng tôi đang trở về một tham chiếu từ LinhaSobre::operator[] để dữ liệu mà nó không sở hữu. Nó không nên can thiệp vào tuổi thọ của dữ liệu (các nhà xây dựng, các destructor).

Bây giờ tôi muốn hiển thị data được lưu trữ bởi một mẫu khác, std::array<float,4> và không tinh khiết float. Ví dụ, đặt tên cho lớp mới LinhaSobre4f:

std::unique_ptr<float[]> data(new float[720]); 
... 
//creates container to iterate 4 array<float, 4> starting from from data[12] 
    LinhaSobre4f l(data.get()+(3*4), 4); 
//sets those elements to {1.5f, 2.5f, 3.5f, 4.5f}; 
    for(size_t i = 0; i < l.size(); i++) 
     l[i] = { {1.5f, 2.5f, 3.5f, 4.5f} }; 

Lưu ý rằng tôi coi các mục là mảng. Điều này sẽ dẫn đến một số thay đổi trong lớp container, mối quan tâm chính của tôi là với operator[], đây là mã lớp đầy đủ:

struct LinhaSobre4f 
{ 
    LinhaSobre4f(float * pos_begin, size_t size_): 
     pos0(pos_begin), 
     size_(size_){} 
    std::array<float, 4> & operator[](size_t i)const 
    { 
     std::array<float,4> * r = 
      reinterpret_cast<std::array<float,4>*> (pos0+(4*i)); 
     return *r; 
    } 
    size_t size()const 
    { 
     return size_; 
    } 
private: 
    float * pos0; 
    size_t size_; 
}; 

Các operator[] trả về một tham chiếu đến một khối bộ nhớ coi là một std::array<float,4> mà không bao giờ thực sự tồn tại như vậy, nhưng với bảo hành bố trí bộ nhớ std::array, nó hoạt động. Tôi không chắc về điều này, có ổn không? (ngoài việc liên kết bộ nhớ, mà tôi sẽ đảm bảo). Tôi có được phép phơi bày một vật như thế này, theo ngữ nghĩa không? Thuật ngữ chính xác cho điều này là gì? (Tôi đã sử dụng đối tượng giả mạo trong tiêu đề).

Here's a live demo of the example. Here's another (the other link sometimes fails)

+0

Một điều chắc chắn nổi bật với tôi (và trình biên dịch sẽ cảnh báo bạn về điều này) là trong 'toán tử []' của 'LinhaSobre4f' bạn đang trả về tham chiếu đến biến tạm thời - bạn nên thay đổi chữ ký để trả về theo giá trị thay vì tham chiếu. – ArchbishopOfBanterbury

+1

(Tôi có?) Biến cục bộ (tạm thời) là một con trỏ, và tôi trả lại tham số của nó, không phải là tham chiếu đến nó. – Kahler

+1

Tôi nghĩ * bạn an toàn, mặc dù tôi không có gì để sao lưu nó. – vu1p3n0x

Trả lời

5

C++ chuẩn (Tôi đang đọc C++ 11) định nghĩa một std::array như sau:

Các điều kiện cho một số tổng hợp (8.5.1) sẽ được đáp ứng.

Bạn không được đảm bảo rằng std::array là POD. Tiêu chuẩn C++ chỉ đảm bảo rằng nó là một lớp tổng hợp.

Dựa trên đó, tôi tin rằng việc bạn sử dụng reinterpret_cast để chuyển đổi mảng POD là float s thành std::array là hành vi không xác định.

Rất có thể nó sẽ hoạt động, với trình biên dịch của bạn, nhưng bạn không được đảm bảo rằng điều này sẽ là di động hoặc hợp pháp.

+0

Tìm kiếm về nó, tôi đã gặp phải [std :: is_pod] (http://en.cppreference.com/w/cpp/types/is_pod), tôi có thể giả định nó an toàn không nếu nó trả về true thành std :: mảng ] (http://ideone.com/ez6sX4)? – Kahler

+0

Nó là an toàn nếu nó trả về true với trình biên dịch hiện tại của bạn. Tuy nhiên, hãy nhớ rằng nếu nó trả về true với trình biên dịch hiện tại của bạn, không có gì đảm bảo rằng nó sẽ trả về true với bất kỳ trình biên dịch nào khác, hoặc bất kỳ phiên bản nào trong tương lai hoặc trước đó của trình biên dịch của bạn. –

2

Bạn có thể tạo một reference_type cũ đồng bằng:

struct LinhaSobre4f { 
    struct Ref { 
     Ref(float *m): m(m){}; 
     Ref &operator=(std::initializer_list<float> const &l) { 
      std::copy(l.begin(), l.end(), m); 
      return *this; 
     } 
    private: 
     float *m; 
    }; 
    Ref operator[](size_t i) { return m + 4 * i; } 
private: 
    float *m; 
}; 
+0

Tôi hiểu phái đoàn, nhưng 'toán tử []' trả về giá trị, không phải là tham chiếu ... Mong muốn của tôi là có thể vận hành trực tiếp trong dữ liệu, ngoài ra 'Ref' bây giờ phải thực hiện mọi hoạt động mong muốn. – Kahler

1

Thêm về câu trả lời Sam Varshavchik, bạn có thể quan tâm đến các loại span (formerly known as array_view).

Các span loại là một sự trừu tượng cung cấp một cái nhìn qua một chuỗi liền kề của các đối tượng, việc lưu trữ thuộc sở hữu của một số đối tượng khác (chi tiết hơn trong P0122R1, CppCoreGuidelinesGuidelines Support Library Review: span<T>).

Khái niệm, một span chỉ đơn giản là một con trỏ tới một số bộ nhớ và số lượng các phần tử có thể truy cập thông qua con trỏ đó. Nó quá nhỏ đến nỗi nó có thể được truyền qua giá trị.

Nguồn mở (chỉ tiêu đề), triển khai tham chiếu có sẵn tại https://github.com/Microsoft/GSL (triển khai thường giả định nền tảng triển khai hỗ trợ C++ 14. Có giải pháp cụ thể để hỗ trợ MSVC 2013 và 2015).

+0

Wow! Điều đó dường như được thực hiện chính xác cho kịch bản của tôi! Tuy nhiên, đó là một lớp tham chiếu ... không truy cập trực tiếp. Tôi đang trở nên tin rằng ngữ nghĩa C++ không bao gồm 'xử lý đoạn này của bộ nhớ như (...) 'trực tiếp. Lớp helper phải mã hóa truy cập bằng cách nào đó. – Kahler

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