Tại sao tôi không thể tạo một vectơ tài liệu tham khảo?


351

Khi tôi làm điều này:

std::vector<int> hello;

Mọi thứ hoạt động tuyệt vời. Tuy nhiên, khi tôi biến nó thành một vectơ tham chiếu thay thế:

std::vector<int &> hello;

Tôi nhận được những lỗi khủng khiếp như

lỗi C2528: 'con trỏ': con trỏ để tham chiếu là bất hợp pháp

Tôi muốn đặt một loạt các tham chiếu đến các cấu trúc vào một vectơ, để tôi không phải can thiệp với các con trỏ. Tại sao vector ném một cơn giận dữ về điều này? Là lựa chọn duy nhất của tôi để sử dụng một vectơ con trỏ thay thế?


46
bạn có thể sử dụng std :: vector <Reference_wrapper <int >> xin chào; Xem informationit.com/guides/content.aspx?g=cplusplus&seqNum=217
amit

2
@amit liên kết không còn hiệu lực, tài liệu chính thức tại đây
Martin

Câu trả lời:


339

Loại thành phần của các thùng chứa như vectơ phải được gán . Tài liệu tham khảo không được gán (bạn chỉ có thể khởi tạo chúng một lần khi chúng được khai báo và bạn không thể làm cho chúng tham chiếu cái gì khác sau này). Các loại không thể gán khác cũng không được phép là thành phần của container, ví dụ như vector<const int>không được phép.


1
Bạn đang nói rằng tôi không thể có một vectơ? (Tôi chắc chắn rằng tôi đã làm điều đó ...)
James Curran

8
Có, một std :: vector <std :: vector <int >> là chính xác, std :: vector có thể gán được.
Martin Côte

17
Thật vậy, đây là lý do "thực tế". Lỗi về T * là không thể của T là U & chỉ là tác dụng phụ của yêu cầu bị vi phạm mà T phải được chỉ định. Nếu vectơ có thể kiểm tra chính xác tham số loại, thì có lẽ nó sẽ nói "yêu cầu bị vi phạm: T & không thể gán được"
Johannes Schaub - litb

2
Kiểm tra khái niệm có thể gán tại boost.org/doc/libs/1_39_0/doc/html/Assignable.html tất cả các hoạt động ngoại trừ trao đổi là hợp lệ trên tài liệu tham khảo.
amit

7
Điều này không còn đúng nữa. Kể từ C ++ 11, yêu cầu độc lập với hoạt động đối với phần tử là "Có thể xóa" và tham chiếu thì không. Xem stackoverflow.com/questions/33144419/ cường .
laike9m

119

vâng, bạn có thể, tìm kiếm std::reference_wrapper, bắt chước một tài liệu tham khảo nhưng có thể gán được và cũng có thể được "nối lại"


4
Có cách nào để gọi xung quanh get()trước khi cố gắng truy cập một phương thức của một thể hiện của một lớp trong trình bao bọc này không? Ví dụ như reference_wrapper<MyClass> my_ref(...); my_ref.get().doStuff();không tham khảo nhiều như thế.
timdiels

3
Không phải nó hoàn toàn có thể được đúc cho Loại bằng cách trả lại tham chiếu?
WorldSEnder

3
Có, nhưng điều đó đòi hỏi một bối cảnh ngụ ý chuyển đổi nào là bắt buộc. Thành viên truy cập không làm điều đó, do đó cần .get(). Những gì timdiels muốn là operator.; có một cái nhìn về các đề xuất / thảo luận mới nhất về điều đó.
gạch dưới

31

Theo bản chất của chúng, các tài liệu tham khảo chỉ có thể được đặt tại thời điểm chúng được tạo; tức là hai dòng sau có tác dụng rất khác nhau:

int & A = B;   // makes A an alias for B
A = C;         // assigns value of C to B.

Hơn nữa, điều này là bất hợp pháp:

int & D;       // must be set to a int variable.

Tuy nhiên, khi bạn tạo một vectơ, không có cách nào để gán giá trị cho các mục của nó khi tạo. Bạn thực chất chỉ là tạo ra một loạt các ví dụ cuối cùng.


10
"Khi bạn tạo một vectơ, không có cách nào để gán giá trị cho các mục của nó khi tạo" Tôi không hiểu ý của bạn về câu nói này. "Các mặt hàng của nó lúc sáng tạo" là gì? Tôi có thể tạo một vector trống. Và tôi có thể thêm các mục bằng .push_back (). Bạn chỉ chỉ ra rằng các tài liệu tham khảo không có khả năng mặc định. Nhưng tôi chắc chắn có thể có các vectơ của các lớp không thể xây dựng mặc định.
newacct

2
Phần tử ype của std :: vector <T> không bắt buộc phải được cấu trúc mặc định. Bạn có thể viết struct A {A (int); riêng tư: A (); }; vectơ <A> a; tốt thôi
Julian Schaub - litb

Và làm thế nào bạn sẽ viết một Push_back () trong trường hợp này? Nó vẫn sẽ sử dụng phân công, không xây dựng.
James Curran

3
James Curran, Không có xây dựng mặc định diễn ra ở đó. đẩy_back chỉ vị trí-tin tức A vào bộ đệm preallocated. Xem tại đây: stackoverflow.com/questions/672352/ Mạnh . Lưu ý rằng yêu cầu của tôi chỉ là vectơ có thể xử lý các loại không thể mặc định. Tất nhiên, tôi không khẳng định rằng nó có thể xử lý T & (dĩ nhiên là không thể).
Julian Schaub - litb

29

Ion Todirel đã đề cập đến một câu trả lời bằng cách sử dụng std::reference_wrapper. Vì C ++ 11, chúng tôi có một cơ chế để lấy đối tượng từ std::vector và xóa tham chiếu bằng cách sử dụng std::remove_reference. Dưới đây được đưa ra một ví dụ được biên dịch bằng cách sử dụng g++clangvới tùy chọn
-std=c++11và được thực hiện thành công.

#include <iostream>
#include <vector>
#include<functional>

class MyClass {
public:
    void func() {
        std::cout << "I am func \n";
    }

    MyClass(int y) : x(y) {}

    int getval()
    {
        return x;
    }

private: 
        int x;
};

int main() {
    std::vector<std::reference_wrapper<MyClass>> vec;

    MyClass obj1(2);
    MyClass obj2(3);

    MyClass& obj_ref1 = std::ref(obj1);
    MyClass& obj_ref2 = obj2;

    vec.push_back(obj_ref1);
    vec.push_back(obj_ref2);

    for (auto obj3 : vec)
    {
        std::remove_reference<MyClass&>::type(obj3).func();      
        std::cout << std::remove_reference<MyClass&>::type(obj3).getval() << "\n";
    }             
}

12
Tôi không thấy giá trị ở std::remove_reference<>đây. Điểm quan trọng std::remove_reference<>là cho phép bạn viết "loại T, nhưng không phải là tài liệu tham khảo nếu nó là một". Vì vậy, std::remove_reference<MyClass&>::typecũng giống như viết MyClass.
alastair

2
Không có giá trị nào trong đó - bạn chỉ có thể viết for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n"; (hoặc for (const MyClass& obj3: vec)nếu bạn khai báo getval()const, như bạn nên).
Toby Speight

14

boost::ptr_vector<int> sẽ làm việc.

Chỉnh sửa: là một đề xuất để sử dụng std::vector< boost::ref<int> >, sẽ không hoạt động vì bạn không thể xây dựng mặc định a boost::ref.


8
Nhưng bạn có thể có một vectơ hoặc các kiểu không mặc định, phải không? Bạn chỉ cần cẩn thận không sử dụng ctor mặc định. của vectơ
Manuel

1
@Quản lý: Hoặc resize.
Các cuộc đua nhẹ nhàng trong quỹ đạo

5
Hãy cẩn thận, các thùng chứa con trỏ của Boost có quyền sở hữu độc quyền của các điểm. Trích dẫn : "Khi bạn cần chia sẻ ngữ nghĩa, thư viện này không phải là thứ bạn cần."
Matthäus Brandl

12

Đó là một lỗ hổng trong ngôn ngữ C ++. Bạn không thể lấy địa chỉ của một tham chiếu, vì cố gắng làm như vậy sẽ dẫn đến địa chỉ của đối tượng được tham chiếu, và do đó bạn không bao giờ có thể đưa con trỏ đến tham chiếu. std::vectorhoạt động với các con trỏ tới các phần tử của nó, vì vậy các giá trị được lưu trữ cần phải có thể được trỏ đến. Bạn sẽ phải sử dụng con trỏ thay thế.


Tôi đoán nó có thể được thực hiện bằng cách sử dụng bộ đệm void * và vị trí mới. Không phải điều này sẽ có nhiều ý nghĩa.
peterchen

55
"Lỗ hổng trong ngôn ngữ" quá mạnh. Đó là do thiết kế. Tôi không nghĩ rằng vectơ phải làm việc với các con trỏ tới các phần tử. Tuy nhiên, phần tử được yêu cầu phải được gán. Tài liệu tham khảo thì không.
Brian Neal

Bạn cũng không thể sizeoftham khảo.
David Schwartz

1
bạn không cần một con trỏ tới một tham chiếu, bạn có tham chiếu, nếu bạn cần con trỏ, chỉ cần giữ con trỏ đó; không có vấn đề gì cần giải quyết ở đây
Ion Todirel

10

TL; DR

Sử dụng std::reference_wrappernhư thế này:

#include <functional>
#include <string>
#include <vector>
#include <iostream>

int main()
{
    std::string hello = "Hello, ";
    std::string world = "everyone!";
    typedef std::vector<std::reference_wrapper<std::string>> vec_t;
    vec_t vec = {hello, world};
    vec[1].get() = "world!";
    std::cout << hello << world << std::endl;
    return 0;
}

Demo

Câu trả lời dài

Như tiêu chuẩn gợi ý , đối với một thùng chứa tiêu chuẩn Xchứa các đối tượng loại T, Tphải Erasabletừ X.

Erasable có nghĩa là biểu thức sau đây được hình thành tốt:

allocator_traits<A>::destroy(m, p)

Alà loại cấp phát của trình chứa, mlà thể hiện của cấp phát và plà một con trỏ của loại *T. Xem ở đây để Erasableđịnh nghĩa.

Theo mặc định, std::allocator<T>được sử dụng như là cấp phát của vector. Với bộ cấp phát mặc định, yêu cầu tương đương với tính hợp lệ của p->~T()(Lưu ý đây Tlà loại tham chiếu và plà con trỏ tới tham chiếu). Tuy nhiên, con trỏ đến một tham chiếu là bất hợp pháp , do đó biểu thức không được hình thành tốt.


3

Như những người khác đã đề cập, có lẽ cuối cùng bạn sẽ sử dụng một vectơ con trỏ.

Tuy nhiên, thay vào đó , bạn có thể muốn xem xét sử dụng ptr_vector !


4
Câu trả lời này là không thể thực hiện được, vì một ptr_vector được coi là một bộ lưu trữ. Đó là nó sẽ xóa các con trỏ lúc gỡ bỏ. Vì vậy, nó không thể sử dụng cho mục đích của mình.
Klemens Morgenstern

0

Như các ý kiến ​​khác cho thấy, bạn bị giới hạn trong việc sử dụng con trỏ. Nhưng nếu nó giúp, đây là một kỹ thuật để tránh đối mặt trực tiếp với con trỏ.

Bạn có thể làm một cái gì đó như sau:

vector<int*> iarray;
int default_item = 0; // for handling out-of-range exception

int& get_item_as_ref(unsigned int idx) {
   // handling out-of-range exception
   if(idx >= iarray.size()) 
      return default_item;
   return reinterpret_cast<int&>(*iarray[idx]);
}

reinterpret_castkhông cần thiết
Xeverous

1
Như các câu trả lời khác cho thấy, chúng tôi không bị giới hạn trong việc sử dụng con trỏ.
gạch dưới
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.