2012-01-02 25 views
19

Các mã sau đây gây nên C4345 trên dòng rõ rệt:Cảnh báo C4345 của Visual Studio có sai không?

#include <array> 
#include <iostream> 

int main(){ 
    static unsigned const buf_size = 5; 
    typedef std::array<char, buf_size> buf_type; 

    char buf[] = { 5, 5, 5, 5, 5 }; 
    void* p = &buf[0]; 
    buf_type* pbuf = new (p) buf_type(); // <=== #10 

    for(unsigned i=0; i < buf_size; ++i) 
     std::cout << (char)((*pbuf)[i] + 0x30) << ' '; 
} 

main.cpp (10): cảnh báo C4345: thay đổi hành vi: một đối tượng kiểu POD được xây dựng với một initializer có dạng() sẽ được mặc định khởi tạo

vì vậy, theo cảnh báo của họ, dòng 10 nên có hành vi tương tự như thể nó đã được viết như

buf_type* pbuf = new (p) buf_type; // note the missing '()' 

Tuy nhiên, đầu ra khác nhau. Cụ thể, phiên bản đầu tiên sẽ in năm 0 s, trong khi phiên bản thứ hai sẽ in năm 5 s. Như vậy, phiên bản đầu tiên thực sự là giá trị khởi tạo (và bộ đệm cơ bản zero-initialized), mặc dù MSVC nói rằng nó sẽ không.

Đây có phải là lỗi trong MSVC không? Hoặc tôi đã giải thích sai sự cảnh báo/mã lỗi của tôi có bị lỗi không?

+9

¤ Cảnh báo đề cập đến việc thiếu khởi tạo trong các phiên bản cũ hơn, tức là cảnh báo, Cảnh báo, CẢNH BÁO !, chúng tôi đã khắc phục lỗi trước đó! Trích dẫn tiêu chuẩn thánh: "Một đối tượng có bộ khởi tạo là một bộ ngoặc đơn trống, tức là,(), sẽ được khởi tạo giá trị", trong trường hợp không có khởi tạo được chỉ định cho một cái gì đó được chuyển thành không khởi tạo. Cảnh báo, tuy nhiên, không chính xác đề cập đến "khởi tạo mặc định" (không chính xác trừ khi vẫn còn một lỗi). Đó có thể là lý do tại sao các câu trả lời ở đây cho đến nay đã được khá bối rối. Chúc mừng & hth., –

Trả lời

13

TL; DR phiên bản: Hành vi của MSVC thực sự chính xác, mặc dù cảnh báo không chính xác (nên nói giá trị được khởi tạo).


Đối new (p) buf_type;, MSVC là đúng để thực hiện khởi tạo mặc định, bởi vì tiêu chuẩn (5.3.4 [expr.new]) đòi hỏi:

Một mới thể hiện tạo ra một đối tượng kiểu T khởi đối tượng đó như sau:

  • Nếu new-initializer bị bỏ qua, đối tượng là mặc định được khởi tạo (8.5); nếu không có khởi tạo được thực hiện, đối tượng có giá trị không xác định.
  • Nếu không, trình khởi tạo mới được diễn giải theo quy tắc khởi tạo 8.5 để khởi tạo trực tiếp.

std::array là loại lớp. Đối với các loại lớp (8.5 [dcl.init]):

Để mặc định-khởi một đối tượng kiểu T có nghĩa là:

  • nếu T là một (có thể cv-đủ điều kiện) kiểu lớp, các nhà xây dựng mặc định cho T được gọi là (và khởi tạo không đúng định dạng nếu T không có hàm tạo mặc định có thể truy cập được);

mặc định-khởi rời khỏi bộ nhớ không thay đổi chỉ dành cho các kiểu dữ liệu (và mảng liệu làm thuốc)

Mặt khác, std::array có một constructor mặc định defaulted, vì vậy các thành viên tự nên được default- được khởi tạo. Và trong thực tế, bạn quan sát thấy điều đó.


Sau đó, theo cùng một phần, phiên bản new (p) buf_type(); gây khởi tạo trực tiếp.

std::array là một tổng hợp, vì vậy tôi nghĩ rằng quy tắc này (8.5.1 [dcl.init.aggr]) sau đó áp dụng:

Nếu có ít initializer-khoản trong danh sách hơn là có những thành viên trong tổng , thì mỗi thành viên không được khởi tạo rõ ràng sẽ được khởi tạo từ danh sách khởi tạo rỗng (8.5.4).

Và điều đó có nghĩa là khởi tạo giá trị cho tất cả các phần tử.

Nope, đây là quy tắc (8,5 [dcl.init]):

Một đối tượng có initializer là một tập rỗng của dấu ngoặc đơn, ví dụ: (), sẽ là giá trị khởi tạo.

Khởi tạo giá trị của việc khởi tạo giá trị phương tiện tổng hợp của tất cả các phần tử, vì các phần tử nguyên thủy, có nghĩa là không điền.

Vì vậy, hành vi của MSVC thực sự chính xác, mặc dù cảnh báo không chính xác (cần nói là giá trị được khởi tạo).

Nó đã được báo cáo, xem

+0

Và ctor mặc định của 'std :: array' được xác định hoàn toàn, có các quy tắc ở đâu đó theo §12.1 IIRC. Một ctor ngầm định mặc định có một danh sách khởi tạo ctor rỗng và câu lệnh ghép (còn gọi là 'name() {}'). Như vậy, mảng phải được bỏ trong uninitialized trong 'new (p) buffer_type;' case (nó là), và trong phiên bản đầu tiên theo cảnh báo (nó không phải là). Nếu những gì bạn trích dẫn sẽ thực sự khởi tạo mảng, thì phiên bản 2 cũng sẽ in tất cả các số không. – Xeo

+0

Trên phần bên dưới bộ chia: Có, nhưng cảnh báo nói rằng nó sẽ mặc định khởi tạo, mà nó không. Câu hỏi này đặc biệt về hành vi của MSVC trong vấn đề đó. – Xeo

+0

http://chat.stackoverflow.com/transcript/message/2251742#2251742 – Xeo

1

Dưới đây là cách tôi đọc bài viết MS http://msdn.microsoft.com/en-us/library/wewb47ee%28v=vs.80%29.aspx

Theo phiên bản cũ của VS (cú pháp ban đầu của bạn) - trong bài viết này, VS 2003 - một khởi tạo POD sẽ mặc định khởi tạo với 0s. Mã của bạn sẽ hỗ trợ điều này.

Theo phiên bản mới của VS - trong bài viết này, đó sẽ là VS 2005 - nó đã được lập trình để khởi tạo POD rõ ràng là không khởi tạo mặc định được thực hiện. Trong trường hợp của bạn, như được hiển thị trong mã của bạn, nó hiển thị chính xác 5s.

Vì vậy, khi tôi đọc nó, cú pháp cũ đã khởi tạo POD thành 0 ngay cả khi mã của bạn đã khởi tạo nó một cách rõ ràng. Tôi làm thêm một tuyên bố từ chối rằng nó rất muộn ở đây và tôi mệt mỏi, vì vậy điều này tất cả có thể được bập bẹ.

+0

Bạn đang hoàn toàn trộn lên giá trị mặc định và khởi tạo ở đây. – Xeo

+0

@Xeo: Không, Microsoft hoàn toàn trộn lẫn giá trị mặc định và khởi tạo giá trị. Đọc các nhận xét bên trong mã mẫu tại liên kết đó. –

-1

cảnh báo này đề cập Nghiêm một sự thay đổi đã xảy ra trong cách Visual Studio sẽ giải thích và lập tuyên bố rằng từ một phiên bản trước. "Thay đổi hành vi" có nghĩa là "nếu bạn đã viết điều này với VC++ cũ hơn, hãy cẩn thận, mọi thứ đã thay đổi". Nó không có nghĩa là để nói rằng ví dụ đầu tiên tương đương với ví dụ thứ hai.

Thực tế, đây là trình biên dịch phù hợp với tiêu chuẩn C++.Nó không phải là một cảnh báo rằng những điều đó S be là tương đương - đó là một cảnh báo rằng họ ONCE WERE nhưng bây giờ, không. Bạn ngoại suy những gì "mặc định khởi tạo" có nghĩa là không chính xác.

Thông thường, đối tượng POD là vị trí mới sẽ KHÔNG được khởi chạy mặc định, vì vậy cảnh báo này là đúng khi cho bạn biết rằng() sẽ làm cho điều đó xảy ra. Tuy nhiên, nó không phải là một cảnh báo chống lại việc này, nhưng như tôi đã nói, Microsoft chú ý một sự thay đổi.

An std :: mảng được đảm bảo (vì vậy tài liệu MSDN) là loại POD khi loại được cung cấp là POD (chẳng hạn như char). Một loại POD về cơ bản là dữ liệu thuần cũ: trình biên dịch xử lý nó như thể nó chỉ là một đối tượng. Mặc dù nó là một lớp, không khởi tạo được thực hiện trên nó trừ khi được gọi một cách rõ ràng, giống như nó sẽ không khởi tạo một con trỏ mảng C chuẩn (và, trên thực tế, không thể).

Chúng ta hãy đi đến một số mã C cho một số ánh sáng.

// POD version. 
// buf_type = new (p) buf_type; 
typedef char buf_type; 
buf_type *pbuf = p;    // Pointer is assigned 

Và,

// Constructed version. 
// buf_type = new (p) buf_type(); 
void construct_buf_type(buf_type *what); 

typedef char buf_type; 
buf_type *pbuf = p;    // Pointer is assigned 
construct_buf_type(buf_type); // Constructor is called which default-initializes it 

gì xảy ra đằng sau hậu trường là một chút khác nhau, nhưng đây là khái niệm chính xác gì xảy ra. Cả hai câu lệnh cho trình biên dịch biết đặt đối tượng tại vị trí đó; một với dấu() ở cuối cho phép nó gọi hàm khởi tạo mặc định sau đó.

Có, điều này có thể là một sự mơ hồ cú pháp khó chịu nếu bạn quên() của bạn trong của bạn mới khi bạn đang viết các đối tượng POD. Nhưng hầu như không ai từng làm, đó là một phần lý do tại sao POD thường bị hiểu sai.

+1

Như @Xeo đã nói với Gerald, bạn đang trộn lên giá trị mặc định và khởi tạo giá trị. Và -1 bởi vì tôi đã trích dẫn các phần có liên quan của tiêu chuẩn, bạn không bận tâm để đọc câu trả lời của tôi. 'buf_type = new (p) buf_type;' mặc định khởi tạo đối tượng, nhưng 'buf_type = new (p) buf_type();' value-initializes nó. –

+0

@BenVoigt Tôi không chắc chắn làm thế nào bạn có thể có được hỗn hợp này ... 'buf_type = new (p) buf_type;' _không khởi tạo object_, tất cả nó làm là nói cho trình biên dịch để xử lý phần đó của bộ nhớ như đối tượng ... nó không phải là khởi tạo mặc định cũng như khởi tạo giá trị mà không có() –

+1

Đọc khối trích dẫn đầu tiên trong câu trả lời của tôi. Nó thẳng từ tiêu chuẩn. * New-initializer * được bỏ qua, vì vậy đối tượng là * mặc định khởi tạo *. Giai đoạn. Kết thúc câu chuyện. Bản thân tiêu chuẩn nói như vậy. –

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