2017-08-28 21 views
41

Trong C++ 98, nguyên mẫu cho hàm tạo điền của std::vector có giá trị mặc định cho bộ khởi tạo.Tại sao C++ 11 loại bỏ giá trị mặc định khỏi các nguyên mẫu của std :: constructor fill của vector?

explicit vector (size_type n, const value_type& val = value_type(), 
       const allocator_type& alloc = allocator_type()); 

C++ 11 sử dụng hai nguyên mẫu.

explicit vector (size_type n); 
     vector (size_type n, const value_type& val, 
       const allocator_type& alloc = allocator_type()); 

(Trong C++ 14 nhà xây dựng fill thay đổi một lần nữa, nhưng nó không phải là điểm của câu hỏi này.)

Một liên kết tham chiếu là here.

Tại sao C++ 11 không dùng giá trị khởi tạo mặc định value_type()?

Nhân tiện, tôi đã cố gắng biên dịch mã sau bằng clang++ -std=c++11 và nó đã phát ra lỗi, có nghĩa là loại giá trị vẫn cần phải có hàm tạo mặc định như S() {}, tức là có thể cấu hình mặc định.

#include <vector> 

struct S { 
    int k; 
    S(int k) : k(k) {} // intentionally remove the synthesized default constructor 
}; 

int main() { 
    std::vector<S> s(5); // error: no matching constructor 
} 
+3

Ví dụ bạn hiển thị ở cuối sẽ không hoạt động trước C++ 11 hoặc vì 'S' không được cấu hình mặc định. Và C++ 11 không * không dùng nữa * giá trị mặc định, hàm tạo duy nhất đó được thay thế bởi hai giá trị khác. – Praetorian

+5

Giá trị mặc định là ác. –

+0

@Praetorian yeah Tôi nên nói C++ 11 * loại bỏ * giá trị mặc định từ nguyên mẫu của nhà xây dựng. – user8385554

Trả lời

50

C++ 98 lấy đối tượng mẫu, sau đó sao chép nó n lần. Theo mặc định, nguyên mẫu là đối tượng được tạo mặc định.

Phiên bản C++ 11 xây dựng n đối tượng được tạo mặc định.

Điều này loại bỏ n bản sao và thay thế bằng n cấu trúc mặc định. Ngoài ra, nó tránh xây dựng nguyên mẫu.

Giả sử lớp học của bạn trông như thế này:

struct bulky { 
    std::vector<int> v; 
    bulky():v(1000) {} // 1000 ints 
    bulky(bulky const&)=default; 
    bulky& operator=(bulky const&)=default; 

    // in C++11, avoid ever having an empty vector to maintain 
    // invariants: 
    bulky(bulky&& o):bulky() { 
    std::swap(v, o.v); 
    } 
    bulky& operator=(bulky&& o) { 
    std::swap(v,o.v); 
    return *this; 
    } 
}; 

đây là một lớp học mà luôn sở hữu một bộ đệm của 1000int s.

nếu chúng ta sau đó tạo ra một vector của bulky:

std::vector<bulky> v(2); 

trong C++ 98 này được phân bổ 3 lần năm 1000 số nguyên. Trong C++ 11, điều này chỉ được phân bổ 2 lần 1000 số nguyên.

Ngoài ra, phiên bản C++ 98 yêu cầu loại có thể sao chép được. Có các loại không thể sao chép trong C++ 11, chẳng hạn như std::unique_ptr<T> và không thể tạo một con trỏ độc đáo được tạo mặc định bằng cách sử dụng chữ ký C++ 98 của vector. Chữ ký C++ 11 không có vấn đề gì với nó.

std::vector<std::unique_ptr<int>> v(100); 

Ở trên sẽ không hoạt động nếu chúng tôi vẫn có phiên bản C++ 98.

+2

Tôi có lẽ sẽ hoàn nguyên giải thích, vì tính chính xác cao hơn hiệu quả: D Nhưng tuy nhiên, một câu trả lời hay cho câu hỏi hay. – SergeyA

+0

@SergeyA Tôi không chắc chắn khi nào chính xác các yêu cầu để được trong một vector mà thoải mái từ được trên các loại thông qua để vector, và thay vào đó kết hợp với các phương pháp chính xác được sử dụng. Sự khác biệt hiệu quả chắc chắn xuất hiện trong C++ 11; Tôi không chắc chắn nếu về mặt kỹ thuật yêu cầu thoải mái đã làm. Vì vậy, tôi đã mô tả chi tiết những gì tôi biết, và sau đó đề cập đến một nơi nào đó trong C++ 11/14 nó cũng dẫn đến các yêu cầu thoải mái trên một loại (trong thực tế trong C++ 11, tôi không chắc liệu tiêu chuẩn có thực sự thư giãn nó trước khi C + 14, bởi vì tôi không nhớ) – Yakk

+0

lời giải thích tốt đẹp, tôi chỉ không hiểu tại sao trong phiên bản C++ 98 đối tượng nguyên mẫu isnt được sử dụng như yếu tố đầu tiên (tức là cũng sẽ chỉ làm 2000 số nguyên được phân bổ trong ví dụ của bạn) – user463035818

47

Lý do hàm tạo được chia làm hai là hỗ trợ các loại "chỉ di chuyển" chẳng hạn như unique_ptr<T>.

constructor này:

vector(size_type n, const T& value, const Allocator& = Allocator()); 

đòi hỏi T được sao chép constructible, vì nT s phải được sao chép từ value để cư vector.

constructor này:

explicit vector(size_type n, const Allocator& = Allocator()); 

không không đòi hỏi T được sao chép constructible, chỉ mặc constructible.

Các constructor sau làm việc với unique_ptr<T>:

std::vector<std::unique_ptr<int>> s(5); 

trong khi các nhà xây dựng trước đây không.

Đây là đề nghị khiến sự thay đổi này: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1858.html#23.2.4.1%20-%20vector%20constructors,%20copy,%20and%20assignment

Và nghiên cứu này có một số lý do, mặc dù là thừa nhận một chút ở phía bên ngắn gọn: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html

Fwiw, resize:

void resize(size_type sz, T c = T()); 

được chia thành:

void resize(size_type sz); 
void resize(size_type sz, const T& c); 

vì lý do chính xác tương tự. Đầu tiên yêu cầu cấu hình mặc định nhưng không thể copy được (để hỗ trợ các kiểu di chuyển mặc định có thể xây dựng mặc định) và thứ hai yêu cầu bản sao có thể xây dựng được.

Những thay đổi này không tương thích ngược 100%. Đối với một số loại (ví dụ: con trỏ thông minh được tính tham chiếu), việc sao chép xây dựng từ đối tượng được tạo mặc định không giống như xây dựng mặc định. Tuy nhiên, lợi ích của việc hỗ trợ các loại di chuyển chỉ được đánh giá là đáng giá chi phí cho việc phá vỡ API này.

+0

Điểm tốt. 'unique_ptr ' là một ví dụ tuyệt vời của lớp không đồng nhất. Rất tiếc, chỉ có thể có một câu trả lời được chấp nhận. – user8385554

+3

chúng tôi sẽ bù đắp bằng upvotes –

+3

Điều này có vẻ như câu trả lời thực sự vì nó giải thích * tại sao * nó đã được thực hiện. (Câu trả lời được chấp nhận hiện tại của Yakk cũng đề cập đến nó, nhưng một thời gian ngắn.) – jamesdlin

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