2011-12-11 56 views
11

Giả sử tôi cóVector cấu trúc với các thành viên const?

#include <string> 
#include <vector> 
using namespace std; 

struct Student 
{ 
    const string name; 
    int grade; 
    Student(const string &name) : name(name) { } 
}; 

Làm cách nào để giữ một vectơ học sinh?

int main() 
{ 
    vector<Student> v; 

    // error C2582: 'operator =' function is unavailable in 'Student' 
    v.push_back(Student("john")); 
} 

Có cách nào để thực hiện việc này hay tôi phải phân bổ tất cả học sinh trên heap và lưu con trỏ cho từng học sinh thay vào đó?

+0

Điều này dường như biên dịch và liên kết với VC 2010. Bạn có thể cung cấp thêm thông tin về môi trường của mình không? Đây có phải là trường hợp thử nghiệm hoàn chỉnh để tái tạo sự thất bại biên dịch đó không? – DRH

+0

@DRH: Tôi đang ở VC 2008, xin lỗi. Và vâng, đó là toàn bộ trường hợp thử nghiệm. – Mehrdad

+0

Trong khi các hoạt động khác, bạn sẽ cần toán tử gán, tôi không thể nghĩ ra bất kỳ lý do nào có thể tại sao 'push_back' lại có yêu cầu đó ... thì lại có thể thực hiện kiểm tra yêu cầu' Assignable'. –

Trả lời

7

Bạn không thể. Loại của bạn vi phạm yêu cầu "Có thể chuyển nhượng" cho các vùng chứa tiêu chuẩn.

ISO/IEC 14882: 2003 23,1 [lib.container.requirements]/3:

Các loại của các đối tượng được lưu trữ trong các thành phần này phải đáp ứng các yêu cầu của CopyConstructible loại (20.1.3), và các yêu cầu bổ sung của các loại Assignable.

Từ bảng 64 (Assignable yêu cầu):

Trong Bảng 64, T là loại sử dụng để nhanh chóng các container, t là một giá trị của T, và u là một giá trị (có thể const) T.

biểu thức: t = u; kiểu trả về: T; hậu điều kiện: t tương đương với u

Về lý thuyết, một tương đương std::vector có thể chọn để làm phá hủy và sao chép xây dựng trong mọi trường hợp, nhưng đó không phải là hợp đồng mà đã được chọn. Nếu không cần phải phân bổ lại thì sử dụng toán tử gán của kiểu chứa cho những thứ như vector::operator=vector::assign có thể hiệu quả hơn đáng kể.

+0

Oh huh ... vì vậy nó cần phải được chuyển nhượng ngay cả khi tôi không bao giờ gán bất cứ điều gì? Không biết điều đó. – Mehrdad

+0

@Philipp: Trên thực tế, vectơ không gán bất cứ điều gì khi phân bổ lại. Nó thường chỉ sao chép hoặc di chuyển-xây dựng phạm vi mới và sau đó xóa một phạm vi cũ. –

+0

Hmm ... Tại sao 'vector' thực hiện các nhiệm vụ sao chép? Nó không thể sao chép công trình + hủy diệt, thay vì sử dụng 'toán tử ='? – Mehrdad

8

Câu trả lời đơn giản là: bạn không thể. Nếu bạn có const biến thành viên, thì trình biên dịch không thể cung cấp toán tử gán bản sao mặc định. Tuy nhiên, nhiều hoạt động mà std::vector cung cấp cần phải thực hiện các bài tập và do đó yêu cầu một toán tử gán bản sao (công khai).

lựa chọn của bạn là:

  1. Hãy name phi const.
  2. Viết toán tử gán bản sao của riêng bạn và nghĩ cách giải quyết "sao chép" thành viên const.
1

Các phần tử của vectơ phải có thể gán bản sao, cấu trúc Student của bạn không phải vì thành viên const. Chỉ cần sử dụng string name thay vì const string name. Trừ khi bạn có yêu cầu cụ thể, các thành viên liên tục trong các lớp học hiếm khi hữu ích. Nếu bạn muốn ngăn chặn các thay đổi cho thành viên, hãy đặt nó ở chế độ riêng tư và thêm một hàm getter công khai.

3

A vector thường cần di chuyển các phần tử xung quanh.Mỗi khi một vector cần phát triển khi bạn gọi push_back(), nó sẽ phân bổ lại bộ nhớ để giữ cho chính nó tiếp giáp và sao chép tất cả các phần tử hiện có vào không gian mới. Ngoài ra, nếu bạn gọi tới số insert() hoặc remove() thì phải chuyển đổi . Đối với vector để có thể thực hiện tất cả các yếu tố phải có khả năng sao chép, có nghĩa là loại bạn lưu trữ trong vectơ phải có toán tử gán được xác định.

Nói chung, nếu bạn định nghĩa một lớp, trình biên dịch sẽ tạo toán tử gán cho lớp đó cho bạn. Tuy nhiên, có những trường hợp khi trình biên dịch không thể làm điều đó. Một trong những trường hợp này là khi lớp có các thành viên liên tục (lưu ý rằng con trỏ-đến-const là ok).

Vì vậy, trong trường hợp của bạn, sự cố là const string name. Nó ngăn cản trình biên dịch tạo ra operator=(), do đó ngăn chặn vector từ biên dịch, mặc dù bạn không thực sự sử dụng phân công trên các phần tử của chính nó.

Một giải pháp là tạo name không const. Cách khác là viết Student::operator=() của riêng bạn, theo một cách nào đó có ý nghĩa. Cách thứ ba là, như bạn đã chỉ ra, sử dụng một vectơ của con trỏ thay vì một vectơ của các đối tượng. Nhưng sau đó bạn phải xử lý phân bổ của họ và de-phân bổ.

P.S. Trường hợp khác khi trình biên dịch không thể tạo ra operator= là khi lớp của bạn có các thành viên tham chiếu.

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