2012-04-18 58 views
11

Tôi đang cố gắng chèn bản sao của phần tử hiện tại vector để tăng gấp đôi. Các mã sau đây làm việc trong các phiên bản trước đó nhưng thất bại trong Visual Studio 2010.Làm cách nào để chèn phần tử trùng lặp vào vectơ?

#include <iostream> 
#include <vector> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 
    test.push_back(1); 
    test.push_back(2); 
    test.insert(test.begin(), test[0]); 
    cout << test[0] << " " << test[1] << " " << test[2] << endl; 
    return 0; 
} 

Output là -17891602 1 2, dự kiến ​​1 1 2.

Tôi đã tìm ra lý do tại sao nó xảy ra - vectơ đang được phân bổ lại và tham chiếu trở nên không hợp lệ trước khi được sao chép vào điểm chèn. Visual Studio cũ dường như đã làm mọi thứ theo thứ tự khác nhau, do đó chứng minh rằng một kết quả có thể có của hành vi không xác định là hoạt động chính xác và cũng chứng minh rằng nó không bao giờ là thứ bạn nên dựa vào.

Tôi đã đưa ra hai cách khác nhau để khắc phục sự cố này. Một là sử dụng reserve để đảm bảo rằng không có phân bổ lại diễn ra:

test.reserve(test.size() + 1); 
    test.insert(test.begin(), test[0]); 

khác là tạo một bản sao từ tài liệu tham khảo để không có sự phụ thuộc vào các tài liệu tham khảo còn lại có giá trị:

template<typename T> 
T make_copy(const T & original) 
{ 
    return original; 
} 

    test.insert(test.begin(), make_copy(test[0])); 

Mặc dù cả hai công việc, không ai cảm thấy giống như một giải pháp tự nhiên. Có cái gì tôi đang mất tích?

+0

Xem trước bản xem trước BTW vc11 cho '1 1 2' cho ví dụ đầu tiên. –

+0

@Jesse, điều đó không làm tôi ngạc nhiên. Quá tải Rvalue của 'chèn' đã được chọn, có vẻ như một lỗi mà họ có thể đã sửa. Mã này hoàn toàn khác nhau giữa quá tải đó và mã mất tham chiếu const. –

+0

Không truyền tới công việc? –

Trả lời

1

Tôi tin rằng đây là hành vi được xác định. Trong §23.2.3 tiêu chuẩn C++ 2011, bảng 100 liệt kê các yêu cầu vùng chứa trình tự và có một mục nhập cho trường hợp này. Nó cung cấp cho biểu thức Ví dụ

a.insert(p,t) 

nơi a là một giá trị của X mà là một loại thùng chứa chuỗi có chứa các yếu tố của loại T, p là một iterator const để a, và t là một giá trị trái hoặc rvalue const loại X::value_type , tức là T.

Khẳng định cho biểu thức này là:

Yêu cầu:T sẽ CopyInsertable vào X. Đối với vectordeque, T cũng phải là CopyAssignable.
Hiệu ứng: Chèn bản sao t trước p.

Các chỉ vector liên quan quote cụ thể tôi có thể tìm được trong §23.3.6.5 đoạn 1:

Ghi chú: Nguyên nhân phân bổ lại nếu kích thước mới lớn hơn công suất cũ. Nếu không có sự tái phân bổ xảy ra, tất cả các vòng lặp và tham chiếu trước khi điểm chèn vẫn hợp lệ.

Mặc dù điều này không đề cập đến vectơ đang được phân bổ lại, nó không tạo ngoại lệ cho các yêu cầu trước đó cho insert trên vùng chứa chuỗi.

Để giải quyết vấn đề này, tôi đồng ý với đề xuất của @ EdChum về việc chỉ tạo bản sao của phần tử và chèn bản sao đó.

+0

Tôi không thấy bất kỳ điều gì trong phần mô tả của bạn gọi ra trường hợp' t' là tham chiếu đến thành viên của 'a'. –

4

Vấn đề là vector::insert lấy tham chiếu đến giá trị làm thông số thứ hai chứ không phải giá trị. Bạn không cần mẫu để tạo bản sao, chỉ cần sử dụng một hàm tạo bản sao để tạo một đối tượng khác, nó sẽ được chuyển qua tham chiếu. Bản sao này vẫn hợp lệ ngay cả khi vectơ được thay đổi kích thước.

#include <iostream> 
#include <vector> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 
    test.push_back(1); 
    test.push_back(2); 
    test.insert(test.begin(), int(test[0])); 
    cout << test[0] << " " << test[1] << " " << test[2] << endl; 
    return 0; 
} 
+0

Đây chỉ là một trường hợp thử nghiệm. Mã thực sự của tôi chứa các phần tử phức tạp hơn nhiều so với một int, và hàm tạo cho một tạm thời chỉ trở nên xấu xí. Đề nghị tốt mặc dù. –

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