2015-04-24 15 views
22

Đoạn mã sau đây là phiên bản đơn giản của nhật ký mà tôi sử dụng. Nó mở rộng std::ostringstream và có thể được điền bằng cách sử dụng << -operator. Sau khi hủy tất cả nội dung được ghi vào std::cout.Tại sao ghi vào đối tượng luồng chuỗi tạm thời chỉ in các địa chỉ đối tượng?

Viết (<<) trực tiếp vào một đối tượng tạm thời, Logger(), tôi mong chờ nó để in mà đầu vào, tuy nhiên, nó chỉ in địa chỉ của một cái gì đó trên std::cout. Khi viết vào một tham chiếu của một đối tượng tạm thời, Logger().stream(), hoạt động như mong đợi.

Tại sao điều đó lại xảy ra?

Btw, hành vi này chỉ xảy ra trong C++ 98-land (ideone), mà tôi phải sử dụng. Với C++ 11 (coliru) và C++ 14 (ideone) cả hai biến thể cuộc gọi đều hoạt động như mong đợi. Có gì khác trong C++ 11/14?

#include <iostream> 
#include <sstream> 

class Logger : public std::ostringstream 
{ 
public: 
    ~Logger() 
    { 
     std::cout << this->str() << std::endl; 
    } 

    Logger& stream() 
    { 
     return *this; 
    } 
}; 

int main(int argc, char ** argv) 
{ 
    // 1. 
    // Prints an address, e.g. 0x106e89d5c. 
    Logger() << "foo"; 

    // 2. 
    // Works as expected. 
    Logger().stream() << "foo"; 

    // What is the difference between 1. and 2.? 

    return 0; 
} 

Trả lời

19

Các operator<< để xử lý chèn của const char * là một tổ chức phi thành viên mẫu:

template< class Traits > 
basic_ostream<char,Traits>& operator<<(basic_ostream<char,Traits>& os, const char* s); 

Phải mất dòng của mình bằng cách không const (vế trái) tham khảo, không liên kết với temporaries.

Trong C++ 98/03, chức năng khả thi tốt nhất là thành viên operator<<(const void *), in một địa chỉ.

Trong C++ 11 và sau đó, thư viện cung cấp một đặc biệt operator<< cho suối rvalue:

template< class CharT, class Traits, class T > 
basic_ostream< CharT, Traits >& operator<<(basic_ostream<CharT,Traits>&& os, 
              const T& value); 

mà không os << value và trả os, về cơ bản thực hiện hoạt động đầu ra trên một dòng giá trị trái.

5

C++ 11 thêm vào này quá tải của non-member operator<<:

template< class CharT, class Traits, class T >  
basic_ostream< CharT, Traits >& operator<<(basic_ostream<CharT,Traits>&& os, 
              const T& value); 

Bây giờ, các nhà điều hành bạn nghĩ rằng bạn đang gọi điện thoại trong trường hợp Logger() là một trong những điều này:

template< class Traits > 
basic_ostream<char,Traits>& operator<<(basic_ostream<char,Traits>& os, 
             const char* s); 

đó làm việc cho Logger().stream() trường hợp vì đó là một tham chiếu lvalue, nhưng điều đó không làm việc cho trường hợp Logger() << "foo". Logger() không thể liên kết với tham chiếu lvalue. Ở đó, sự quá tải khả thi duy nhất là memberoperator<<:

basic_ostream& operator<<(const void* value); 

để in địa chỉ.

11

sự kiện liên quan:

  1. Logger() là một rvalue, nhưng Logger().stream() là một giá trị trái.
  2. Các operator<< mà phải mất một con trỏ và in địa chỉ của nó là thành viên của ostream&, trong khi operator<< mà phải mất một const char* và in chuỗi là một chức năng miễn phí,

    template<class traits> 
    basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, 
    const char* s); 
    

Lưu ý rằng người đầu tiên đối số là một tham chiếu không phải lvalue, vì vậy nó không thể liên kết với một giá trị. Vì vậy, nếu dòng là một rvalue, quá tải này là không khả thi. Do đó, const char* được chuyển đổi thành const void* và địa chỉ của nó được in. Khi bạn sử dụng Logger().stream(), đây là một lvalue, quá tải này sẽ thắng và chuỗi được in.

Trong C++ 11, một nhà điều hành chèn dòng rvalue mới được thêm vào:

template <class charT, class traits, class T> 
basic_ostream<charT, traits>& 
operator<<(basic_ostream<charT, traits>&& os, const T& x); 

với hiệu ứng os << x. Bây giờ, chiến thắng quá tải này trong Logger() << "foo" và chuyển tiếp đối số như là luồng là một giá trị. Sau đó, các chức năng miễn phí trước đây được gọi là được gọi.

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