2010-03-12 9 views
13

Hãy xem xét các chức năng sau:Quay stringstream tạm thời để c_str() trong câu lệnh

void f(const char* str); 

Giả sử tôi muốn tạo ra một chuỗi sử dụng stringstream và vượt qua nó để chức năng này. Nếu tôi muốn làm điều đó trong một tuyên bố, tôi có thể thử:

f((std::ostringstream() << "Value: " << 5).str().c_str()); // error 

Điều này cho biết lỗi: 'str()' không phải là thành viên của 'basic_ostream'. OK, vậy nhà điều hành < < đang trở lại ostream thay vì ostringstream - làm thế nào về việc đưa nó trở lại một ostringstream?

1) Truyền này có an toàn không?

f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output 

Bây giờ với điều này, nó quay ra cho các nhà điều hành < < ("giá trị gia tăng:") cuộc gọi, nó thực sự kêu gọi hành ostream của < < (void *) và in một địa chỉ hex. Điều này là sai, tôi muốn văn bản.

2) Tại sao toán tử < < trên std tạm thời :: ostringstream() gọi toán tử ostream? Chắc chắn tạm thời có một loại 'ostringstream' không phải là 'ostream'?

Tôi có thể truyền tạm thời để thực hiện cuộc gọi của nhà điều hành chính xác!

f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str()); 

Điều này dường như làm việc và vượt qua "Giá trị: 5" đến f().

3) Hiện tại tôi có dựa vào hành vi không xác định không? Các phôi trông khác thường.


Tôi biết phương án tốt nhất là một cái gì đó như thế này:

std::ostringstream ss; 
ss << "Value: " << 5; 
f(ss.str().c_str()); 

... nhưng tôi quan tâm đến hành vi làm việc đó trong một dòng. Giả sử ai đó muốn tạo macro (đáng ngờ):

#define make_temporary_cstr(x) (static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << x).str().c_str()) 

// ... 

f(make_temporary_cstr("Value: " << 5)); 

Chức năng này có được mong đợi không?

+0

Trùng lặp có thể xảy ra do nhầm lẫn chuyển đổi [stringstream, string và char \ *] (http://stackoverflow.com/questions/1374468/stringstream-string-and-char-conversion-confusion). – jww

Trả lời

14

Bạn không thể truyền luồng tạm thời tới std::ostringstream&. Nó không đúng định dạng (trình biên dịch phải cho bạn biết rằng nó sai). Tuy nhiên, những điều sau có thể thực hiện:

f(static_cast<std::ostringstream&>(
    std::ostringstream().seekp(0) << "Value: " << 5).str().c_str()); 

Điều đó tất nhiên là xấu. Nhưng nó cho thấy nó có thể hoạt động như thế nào. seekp là một hàm thành viên trả về std::ostream&.Có lẽ tốt hơn để viết những dòng này thường

template<typename T> 
struct lval { T t; T &getlval() { return t; } }; 

f(static_cast<std::ostringstream&>(
    lval<std::ostringstream>().getlval() << "Value: " << 5).str().c_str()); 

Lý do mà không có bất cứ điều gì nó có void*, là bởi vì đó operator<< là thành viên-chức năng. operator<< mất char const* thì không.

+0

Đoạn mã của bạn với hàm tìm kiếm (0) trả về một chuỗi rỗng cho tôi - nhưng mẫu lval xuất hiện để hoạt động chính xác. Điều đó dựa hoàn toàn vào hành vi được xác định sau đó, và sẽ được di động? – AshleysBrain

+0

@Ashley, chắc chắn là tất cả được xác định. –

+0

Giá trị có thể có toán tử chuyển đổi thành T &, tránh cuộc gọi chức năng? (hoặc có lẽ trình biên dịch không tự động chọn toán tử đó ...) – Macke

3

Tạm thời không thể chuyển thành tham chiếu không const cho hàm, đó là lý do tại sao nó không tìm thấy toán tử trực tuyến chính xác và thay vào đó lấy toán tử void * (nó là hàm thành viên và do đó gọi nó là tạm thời là OK).

Điều gì xảy ra với việc vượt qua các giới hạn bằng cách truyền, tôi có cảm giác rằng đó là UB thực tế, nhưng tôi không thể nói chắc chắn. Một người khác chắc chắn sẽ trích dẫn tiêu chuẩn.

+0

Tôi không đi qua các ostringstream tạm thời để f() - Tôi chỉ đi qua kết quả của c_str(), đó là const (nếu nó quan trọng). Bên cạnh đó, ngay cả như vậy, những gì về điều đó có nghĩa là nó chọn đối số void *? Có gì khác biệt về nó cho phép nó được chọn nhưng không phải là đối số const char *? – AshleysBrain

+2

Bạn đang cố gắng chuyển một ostringstream tạm thời tới ostream & operator << (ostream &, char const *) và thất bại (không có cast bạn đã thực hiện để sửa chữa nó). ostream & ostream :: operator << (void const *) có thể được chọn bởi vì nó là một hàm thành viên và gọi các hàm thành viên của thời gian được cho phép. – Tronic

0

Nếu bạn thích câu one_lined bạn có thể viết:

// void f(const char* str); 
f(static_cast<ostringstream*>(&(ostringstream() << "Value: " << 5))->str()); 

Tuy nhiên bạn nên chọn dễ dàng hơn để duy trì mã như:

template <typename V> 
    string NumberValue(V val) 
    { 
    ostringstream ss; 
    ss << "Value: " << val; 
    return ss.str(); 
    } 
f(NumberValue(5)); 
+0

Một lớp lót bạn đã đề xuất là sai và sẽ không hoạt động đối với tất cả các trường hợp. – missingfaktor

0

tôi sử dụng một cái gì đó một chút như thế này cho khai thác gỗ.

#include <sstream> 

using namespace std; 

const char *log_text(ostringstream &os, ostream &theSame) 
{ 
    static string ret; // Static so it persists after the call 
    ret = os.str(); 
    os.str(""); // Truncate so I can re-use the stream 
    return ret.c_str(); 
} 


int main(int argc, char **argv) 
{ 
    ostringstream ss; 
    cout << log_text(ss, ss << "My first message") << endl; 
    cout << log_text(ss, ss << "Another message") << endl; 
} 

Output:

thông điệp đầu tiên của tôi

Một thông điệp

+1

Tại sao chuyển đổi thành 'const char *' khi bạn có thể chèn 'std :: string' vào luồng? – quantum

1

Điều này có thể được thực hiện bằng một hàm lambda C++ 11.

#include <iostream> 
#include <sstream> 

void f(const char * str) 
{ 
    std::cout << str << std::endl; 
} 

std::string str(void (*populate)(std::ostream &)) 
{ 
    std::ostringstream stream; 
    populate(stream); 
    return stream.str(); 
} 

int main(int argc, char * * args) 
{ 
    f(str([](std::ostream & ss){ ss << "Value: " << 5; }).c_str()); 
    return 0; 
} 

// g++ -std=c++11 main.cpp -o main 
// ./main 
// Value: 5