11

xem xét ví dụ tối thiểu sau đây:Trình tự giải quyết tình trạng quá tải toán tử liên quan đến temporaries

#include <iostream> 

using namespace std; 

class myostream : public ostream { 
    public: 
     myostream(ostream const &other) : 
      ostream(other.rdbuf()) 
     { } 
}; 

int main() { 
    cout << "hello world" << endl; 

    myostream s(cout); 
    s << "hello world" << endl; 

    myostream(cout) << "hello world" << endl; 
} 

Kết quả, cả trên g ++ và Visual C++, là

hello world 
hello world 
0x4012a4 

Các phiên bản đó ghi vào một đối tượng tạm thời , myostream(cout), dường như thích toán tử thành viên ostream::operator<<(void *), thay vì toán tử miễn phí operator<<(ostream &, char *). Dường như tạo sự khác biệt cho dù đối tượng có tên hay không.

Tại sao điều này lại xảy ra? Và làm cách nào để ngăn chặn hành vi này?

Chỉnh sửa: Tại sao điều này xảy ra hiện rõ ràng với nhiều câu trả lời khác nhau. Về cách ngăn chặn điều này, những điều sau đây có vẻ hấp dẫn:

class myostream : public ostream { 
    public: 
     // ... 
     myostream &operator<<(char const *str) { 
      std::operator<<(*this, str); 
      return *this; 
     } 
}; 

Tuy nhiên, điều này dẫn đến tất cả các loại không rõ ràng.

+0

Bạn có thể cân nhắc câu trả lời này đến câu hỏi khác như là một điểm khởi đầu cho một cái gì đó ít nhất cũng tương tự như những gì bạn muốn đạt được: http://stackoverflow.com/questions/469696/what-is-your-most-useful-cc-snippet/470999#470999 Bạn sẽ phải thêm chức năng cho lớp học để chấp nhận các biến tố đầu vào (std :: hex, std :: endl ...), nhưng điều đó không quá khó. –

Trả lời

6

rvalues ​​không thể bị ràng buộc để tham chiếu không const. Vì vậy, trong ví dụ của bạn, tạm thời kiểu ostream không thể là đối số đầu tiên của toán tử miễn phí < < (std :: ostream &, char const *) và những gì được sử dụng là toán tử thành viên < < (void *).

Nếu bạn cần, bạn có thể thêm một cuộc gọi như

myostream(cout).flush() << "foo"; 

mà sẽ chuyển đổi rvalue thành một tài liệu tham khảo.

Lưu ý rằng trong C++ 0X, việc giới thiệu tham chiếu rvalue sẽ cho phép quá tải toán tử < < tham chiếu rvalue làm tham số, giải quyết nguyên nhân gốc của sự cố.

+0

Ý của bạn là "giá trị không thể bị ràng buộc ..."? và "... biến rvalue thành một giá trị ..."? –

+0

Phải, tôi đã sửa phản hồi của mình. Cảm ơn. – AProgrammer

3

Tôi vừa nhận ra phần câu trả lời. Tạm thời không phải là một lvalue, do đó, nó không thể được sử dụng như một đối số của loại ostream &.

Câu hỏi đặt ra "làm thế nào tôi có thể làm công việc này" vẫn ...

+0

Cách đơn giản nhất: cung cấp các chức năng thành viên để tạo ra công việc cho bạn. –

7

Nếu một đối tượng không có một tên (ví dụ: nó là một tạm thời), nó không thể bị ràng buộc để một tham chiếu không const. Cụ thể, nó không thể bị ràng buộc để các tham số đầu tiên của:

operator<<(ostream &, char *) 
+0

.. cho đến khi C++ 0x xuất hiện, và trao quyền cho chúng tôi với các tham chiếu giá trị r. – dirkgently

-1

Vâng, tôi không biết ++ đặc tả C gây ra điều này, nhưng nó rất dễ dàng để Suss hiểu tại sao nó xảy ra.

Một cuộc sống tạm thời trên ngăn xếp, thường được chuyển đến một chức năng khác hoặc để thực hiện một thao tác đơn lẻ. Vì vậy, nếu bạn gọi tổng đài miễn phí trên nó:

hành < < (myostream (cout))

Nó bị phá hủy vào cuối của hoạt động này và các nhà điều hành "< <" thứ hai để nối thêm các endl sẽ tham chiếu một đối tượng không hợp lệ. Giá trị trả về từ toán tử "< <" miễn phí sẽ là tham chiếu đến đối tượng tạm thời bị hủy. Đặc tả C++ có thể định nghĩa các quy tắc về các toán tử tự do để ngăn chặn kịch bản này gây khó chịu và gây nhầm lẫn cho các lập trình viên C++.

Hiện tại, trong trường hợp nhà điều hành "< < (void *)" tạm thời, giá trị trả về là chính đối tượng, vẫn còn trên ngăn xếp và không bị hủy, do đó trình biên dịch không biết hủy nhưng để chuyển nó cho người điều hành thành viên tiếp theo, người điều khiển lấy endl. Toán tử chuỗi ngày tạm thời là một tính năng hữu ích cho mã C++ ngắn gọn, vì vậy tôi chắc chắn các nhà thiết kế spec C++ đã xem xét nó và triển khai trình biên dịch để hỗ trợ nó một cách có chủ ý.

chỉnh sửa

Một số người nói rằng nó là để làm với một tham chiếu không const.Mã này biên dịch:

#include <iostream> 
using namespace std; 
class myostream : public ostream { 
    public: 
     myostream(ostream const &other) : 
      ostream(other.rdbuf()) 
     { } 
      ~myostream() { cout << " destructing "; } 
    }; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works"); 
    std::operator << (result, "illegal"); 
     return 0; 
} 

Và nó trả

This works destructing illegal 
+1

Xin lỗi - sự hiểu biết của bạn về thời gian tồn tại của thời gian là sai. –

+0

Xin vui lòng khai sáng cho tôi. AFAIK, mô tả ở đây phù hợp với lời giải thích của tôi: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr382. htm. Nếu bạn sử dụng một phương pháp tĩnh trên một tạm thời, tạm thời phải bị phá hủy khi ngăn xếp cuộc gọi cho hoạt động đó được làm sạch. Nếu nó được sử dụng như là "this" cho một hoạt động thành viên, thì nó có thể tiếp tục sống sau khi hoạt động đã hoàn thành vì nó ở dưới cùng của ngăn xếp cuộc gọi, do đó các toán tử thành viên sẽ làm việc nhưng các phương thức tĩnh sẽ không. –

+0

Liên kết bạn đăng là một nguồn thông tin C++ kém phổ biến (và thể hiện hành vi kỳ lạ, lật tôi đến một trang khác). C++ Standard là tham chiếu ở đây, và nó nói (hiệu quả) rằng tạm thời phải treo xung quanh cho đến khi kết thúc biểu thức hoàn chỉnh nó là một phần của, trong trường hợp này là chuỗi hoàn chỉnh của << toán tử. Lý do thực sự của mã không hoạt động như mong đợi được đưa ra trong câu trả lời của tôi. –

1

Kể từ khi không ai trong số các câu trả lời dường như cho đến nay để đưa ra một giải pháp làm sạch, tôi sẽ giải quyết cho các giải pháp bẩn:

myostream operator<<(myostream stream, char const *str) { 
    std::operator<<(stream, str); 
    return stream; 
} 

này chỉ có thể vì myostream có một constructor sao chép. (Trong nội bộ, nó được hỗ trợ bởi một số đếm ngược std::stringbuf.)

0

Trong khi C++ 11 giải quyết vấn đề này vì có các tham chiếu rvalue, tôi nghĩ đây có thể là giải pháp cho pre-C++ 11.

Giải pháp là phải có một hàm thành viên < < điều hành mà chúng ta có thể đúc đến một tham chiếu không const đến lớp cơ sở:

class myostream : public ostream { 
    public: 
     // ... 
     template<typename T> 
     ostream &operator<<(const T &t) { 
      //now the first operand is no longer a temporary, 
      //so the non-member operators will overload correctly 
      return static_cast<ostream &>(*this) << t; 
     } 
}; 
Các vấn đề liên quan