2009-10-23 35 views
8

Dưới đây là một ví dụ mã tối thiểu mà minh họa các vấn đề:Tại sao cảnh báo này từ trình biên dịch C/C++ XL của IBM?

#include <iostream> 

class Thing 
{ 
    // Non-copyable 
    Thing(const Thing&); 
    Thing& operator=(const Thing&); 

    int n_; 

public: 
    Thing(int n) : n_(n) {} 

    int getValue() const { return n_;} 
}; 

void show(const Thing& t) 
{ 
    std::cout << t.getValue() << std::endl; 
} 

int main() 
{ 
    show(3); 
} 

Điều này mang lại những lỗi tương tự:

int main() 
{ 
    show(Thing(3)); 
} 

IBM XL C/C++ 8.0 biên dịch dưới AIX phát ra những cảnh báo này:

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed. 
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed. 
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible. 

Tôi cũng đã thử g ++ 4.1.2 với "-Wall" và "-pedantic" và không có chẩn đoán. Tại sao truy cập vào hàm tạo bản sao được yêu cầu ở đây? Làm thế nào tôi có thể loại bỏ cảnh báo, ngoài việc làm cho đối tượng có thể sao chép (nằm ngoài tầm kiểm soát của tôi) hoặc tạo một bản sao rõ ràng để chuyển (khi đối tượng thực tế đắt tiền sao chép)?

+0

Bạn có thực sự đang sử dụng trình tạo bản sao đó trong quá trình triển khai lớp ở đâu đó không? –

+0

No. Tôi đã đăng toàn bộ tệp mã đã tạo ra chẩn đoán. –

+0

FYI: Tôi cũng đã thử với VC++ 2005 & 2008, biên soạn không có cảnh báo. Vì vậy, có vẻ như bạn là đúng, vấn đề dường như là IBM biên tập cụ thể – jdehaan

Trả lời

9

Các quy tắc cho điều này là trong §8.5.3/5 của tiêu chuẩn. Có ba tình huống cơ bản được xác định. Việc đầu tiên liên quan đến initializer ('3' trong trường hợp của bạn) hoặc là một lvalue, hoặc có loại lớp. Vì không có điều nào trong số đó là đúng, những gì bạn có là trường hợp thứ ba: khởi tạo tham chiếu const với một giá trị không có loại lớp. Trường hợp này được bao phủ bởi viên đạn cuối cùng trong 8.5.3/5:

Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy initialization (8.5). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 must be the same cv-qualification as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed.

Edit: rereading, tôi nghĩ rằng IBM đã đúng. Tôi đã từng nghĩ đến khả năng phải sao chép tạm thời, nhưng đó không phải là nguồn gốc của vấn đề. Để tạo việc khởi tạo bản sao không tham chiếu tạm thời như được quy định trong §8.5, nó cần ctor bản sao. Đặc biệt, tại thời điểm này, nó tương đương với một biểu thức như:

T x = a;

Điều này về cơ bản tương đương với:

T x = T (a);

I.e. nó được yêu cầu để tạo tạm thời, sau đó sao chép tạm thời đối tượng đang được khởi tạo (trong trường hợp này, là cũng tạm thời là). Để tóm tắt quá trình cần thiết, đó là tương đương với mã như:

T temp1(3); 
T temp2(temp1); // requires copy ctor 
show(temp2); // show's reference parameter binds directly to temp2 
+0

Thông tin tốt. Cảm ơn bạn đã nghiên cứu, Jerry. –

+0

Điều đó thật kỳ lạ, nhưng tôi đồng ý với việc đọc/đọc chuẩn của IBM của bạn. –

+0

bắt buộc phải tạo tạm thời Về mặt kỹ thuật: Cần thiết để __be có thể tạo tạm thời. Về mặt kỹ thuật, trình biên dịch có thể tối ưu hóa điều này. –

3

C++ cho phép trình biên dịch đủ thông minh để tránh sao chép các đối tượng tạm thời, vi phạm quy tắc as-if được phép theo tiêu chuẩn. Tôi không quen thuộc với trình biên dịch AIX C++ của IBM, nhưng có vẻ như nó cho rằng cuộc gọi show(3) yêu cầu một điều tạm thời được sao chép. Trong trường hợp đó, C++ yêu cầu bạn có một hàm tạo bản sao có thể truy cập ngay cả khi trình biên dịch của bạn đủ thông minh để tránh sử dụng nó.

Nhưng tại sao show(3) yêu cầu bản sao ngay từ đầu? Điều đó tôi không thể tìm ra. Với may mắn, litb sẽ được đi cùng trong một chút.

+0

Đó là khá nhiều những gì tôi đã suy nghĩ. –

+0

"Nhưng tại sao chương trình (3) yêu cầu một bản sao ở nơi đầu tiên?" Tôi không nghĩ rằng nó nên. Mã nên tạo một đối tượng tạm thời và liên kết trực tiếp với tham số hàm 'const Thing &'. Vì vậy, tôi nghĩ Jerry là đúng: đây là một lỗi. – sbi

+0

Tôi đã thay đổi quyết định - sau khi đọc lại tiêu chuẩn, tôi chắc chắn đó không phải là lỗi. –

0

Điều gì sẽ xảy ra nếu bạn thử đặt tên là Điều tạm thời?

Thing temp(3);
show(temp);

+0

Điều đó giúp loại bỏ các tin nhắn. Chúng tôi có thể sẽ làm về cơ bản này, nhưng có lẽ với một phân bổ đống. Nó không phải là nhanh, nhưng các đối tượng thực tế cuộc sống là khá lớn để được đưa vào ngăn xếp. Vì vậy, có lẽ tốt hơn là không nên dựa vào tạm thời. –

1

cảm giác ruột của tôi là Jerry answer là đúng, nhưng có một vài câu hỏi vẫn còn.

Điều thú vị là có một vấn đề cốt lõi bao gồm đoạn trước đó của phần đó (391).Vấn đề đó liên quan đến khi đối số là cùng loại lớp. Cụ thể:

int main() { 
    show (Thing (3));  // not allowed under current wording 
          // but allowed with Core Issue 391 

    show (3);    // Still illegal with 391 
} 

Sự thay đổi trong vấn đề cốt lõi 391 chỉ ảnh hưởng đến nơi giá trị tạm thời có cùng loại lớp. Từ ngữ trước đó có:

If the initializer expression is an rvalue, with T2 a class type, and cv1 T1 is reference-compatible with cv2 T2, the reference is bound as follows:

[...]

The constructor that would be used to make the copy shall be callable whether or not the copy is actually done.

Dòng cuối cùng là điều sẽ làm cho show(Thing(3)) bất hợp pháp theo tiêu chuẩn hiện hành. Các từ ngữ đề xuất cho phần này là:

If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2", the reference is bound to the object represented by the rvalue (see 3.10 [basic.lval]) or to a sub-object within that object.

Tại thời điểm này, tôi cho rằng g ++ có thể được cập nhật hành vi của nó theo 391 nhưng rằng sự thay đổi vô tình bao gồm các trường hợp sao chép khởi tạo. Tuy nhiên, đó là không được chứng minh bởi các phiên bản của g ++ mà tôi thử nghiệm với:

class A{ 
public: 
    A(); 
    A (int); 
private: 
    A (A const &); 
}; 

void foo (A const &); 

void foo() 
{ 
    A a = 3 ;  // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR) 

    foo (3) ; // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK) 
    foo (A()); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK) 
    foo (A(3)); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK) 
} 

tôi không thể tìm thấy lỗi trong việc giải thích Jerry đối với trường hợp foo (3), tuy nhiên, tôi không có nghi ngờ do discrepency giữa hành vi biên dịch khác nhau .

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