2013-04-02 25 views
5

Tôi đang cố gắng in đẹp một container STL. Những gì tôi đang cố gắng để in elemets của một container tách biệt với một dấu phân cách. Tuy nhiên tôi đã gặp một số vấn đề.Mẫu quá tải nhà điều hành ostream

1. g ++ vs VC++

ostream& operator<<(ostream& o, const vector<string>& v) { 
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,",")); 
} 

int main() 
{ 

    vector<string> s_v; 
    s_v.push_back("one"); 
    s_v.push_back("two"); 

    cout << s_v; 

} 

g ++ (gcc phiên bản 4.4.0 trên mingw32) có thể biên dịch nó một hoạt động tốt. VC++ (Visual Studio 9) không thể biên dịch mã này.

error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const std::string' (or there is no acceptable conversion) 
1>  c:\program files (x86)\microsoft visual studio 9.0\vc\include\ostream(653): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' 
1>  with 
1>  [ 

tại sao điều này? Mã này có phải là bất hợp pháp không? hoặc nó chỉ là VC++ beign VC++?


2. Biên dịch ngắt biến mẫu chưa sử dụng.

nếu bây giờ tôi thêm một mẫu để các ostream như thế này (nó không được sử dụng, chỉ cần ngồi đó)

template <typename T> // <----- Here 
ostream& operator<<(ostream& o, const vector<string>& v) { 
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,",")); 
} 

int main() 
{ 

    vector<string> s_v; 
    s_v.push_back("one"); 
    s_v.push_back("two"); 

    cout << s_v; 

} 

gcc không thể phù hợp với các nhà điều hành nữa.

error: no match for 'operator<<' in 'std::cout << s_v' 

and a lot more candidates... 

Tại sao? mẫu không được sử dụng. Nó có quan trọng không?


EDIT: Giải pháp này được giải quyết. Tôi phải trở về o;

3. qua sử dụng mẫu

template <typename T> 
ostream& operator<<(ostream& o, const vector<T>& v) { 
    copy(v.begin(), v.end(), std::ostream_iterator<T>(o,",")); 

    return o; // Edited 
} 

int main() 
{ 

    vector<string> s_v; 
    s_v.push_back("one"); 
    s_v.push_back("two"); 

    vector<int> i_v; 
    i_v.push_back(1); 
    i_v.push_back(2); 

    cout << s_v; 
    cout << i_v; 
} 

Nếu tôi biết sử dụng các kiểu mẫu. g + + có thể biên dịch nó nhưng sau đó chấm dứt với một ngoại lệ.

terminate called after throwing an instance of 'std::bad_cast' 
    what(): std::bad_cast 

VC++ chỉ đang ngồi và xem gcc làm tất cả những việc này. Không biên dịch bất kỳ thứ gì trong số họ.

Ai đó có thể làm rõ điều này cho tôi không? Cảm ơn bạn.

+0

Xác định toán tử của bạn trong không gian tên std. Bạn sẽ thấy sự khác biệt – PiotrNycz

+0

Nếu trình biên dịch không thể suy ra loại đối số mẫu, bạn phải tự cung cấp nó. Vì vậy, nếu bạn có 'mẫu void Foo() {...}', bạn cần sử dụng 'Foo ()'. Rõ ràng, bạn không thể làm điều đó khi bạn quá tải một toán tử. Khi bạn _do_ sử dụng 'T', trình biên dịch có thể suy ra kiểu của nó, và đây là lý do tại sao nó hoạt động. – zneak

+0

Ngoài ra, ví dụ cuối cùng của bạn [chạy tốt] (http://ideone.com/mLJcTI) trên ideone. Bạn có thể muốn thêm 'return o;' vào toán tử '' của bạn. Điều này rất có thể sẽ làm hỏng chương trình của bạn nếu bạn đã làm 'cout << s_v << i_v'. – zneak

Trả lời

5

PREMISE:

Trước hết, mã này là bất hợp pháp vì nó bỏ sót một tuyên bố return (đó là khả năng những gì đang gây ra ngoại lệ đó được nêu ra trong phiên bản thứ ba):

ostream& operator<<(ostream& o, const vector<string>& v) { 
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,",")); 
    return o; // <== THIS ONE WAS MISSING 
} 

Điều này sẽ đưa hành vi không xác định vào chương trình của bạn. Mỗi Đoạn 6.6.3/1 của tiêu chuẩn C++ 11, trên thực tế:

[...] Chảy ra khỏi đầu của một hàm tương đương với giá trị trả về không có giá trị; kết quả này trong hành vi không xác định trong hàm trả về giá trị.

Về câu hỏi đầu tiên của bạn:

Một khi điều đó là cố định, mã của bạn là tốt, và thực hiện các bộ thư viện chuẩn được vận chuyển với VC9 có thể có một lỗi. Trong thực tế, trình biên dịch nên tìm kiếm các tình trạng quá tải hợp lệ của operator << trong không gian tên của các đối số (std) và trong không gian tên nơi cuộc gọi đang được thực hiện (không gian tên chung). Miễn là toán tử của bạn được xác định trong không gian tên toàn cầu tuyên bố cout << s_v nằm trong không gian tên chung, độ phân giải quá tải sẽ chọn thành công quá tải của bạn.

Về câu hỏi thứ hai của bạn:

Tại sao? mẫu không được sử dụng. Nó có quan trọng không?

Thật đơn giản: trình biên dịch không có cách nào để khấu trừ T từ đối số hàm, vì vậy trừ khi bạn chỉ định rõ ràng, điều này sẽ dẫn đến lỗi biên dịch. Tuy nhiên, quy định cụ thể mẫu tranh luận một cách rõ ràng có nghĩa là làm điều gì đó như sau, trong đó gần phi nghĩa:

::operator << <void>(std::cout, s_v); 

Trong C++ 11 bạn có thể chỉ định một đối số mặc định cho T, trong đó sẽ thực hiện cuộc gọi chức năng pháp lý, lỗi sau đó một lần nữa, cho mục đích gì?

Về câu hỏi thứ ba của bạn:

Khi T được sử dụng trong các loại ít nhất một trong các thông số chức năng trong một bối cảnh suy luận, trình biên dịch sẽ cho phép suy ra nó từ các đối số chức năng (trong trường hợp này , nó sẽ suy T = std::string, và bạn không cần phải xác định nó một cách rõ ràng

KẾT LUẬN:.

Vì vậy, để tóm tắt: sau khi thêmcần thiếttuyên bố, phiên bản đầu tiên và thứ ba của chương trình của bạn là hợp pháp và có ý nghĩa, trong khi thứ hai là không và không.

+0

Về mã mà anh ta thực sự đã đăng cho câu hỏi đầu tiên: nó sẽ không biên dịch với bất kỳ trình biên dịch nào, vì thiếu bao gồm. Tôi đã nhìn thấy độ phân giải quá tải lạ với VC++ khi bao gồm bị thiếu: bao gồm '' có thể cung cấp một số (nhưng không phải tất cả) của '', vì vậy một số công việc, nhưng những thứ khác thì không. –

+0

@JamesKanze: Phải, tôi giả định OP đã nhập đúng tiêu đề và tất cả những tiêu đề cần thiết, nhưng đúng là điều này không thể được bảo đảm vì phần sơ bộ bị thiếu trong văn bản câu hỏi –

+0

Tôi đang sử dụng std; và tôi có sự bao gồm thích hợp. –

1
  1. Khi được đăng, mã của bạn không được biên dịch với bất kỳ trình biên dịch nào. Bạn đang thiếu bao gồm, và rất nhiều std::. Điều tôi nghi ngờ đang xảy ra là bạn không có tất cả các yêu cầu cần thiết bao gồm; đặc biệt, thiếu #include <string>, và g ++ đó sẽ chọn gián tiếp. Đó là tò mò. Các vấn đề thường là nghịch đảo: rằng VC++ chọn lên rất nhiều thêm bao gồm. Tuy nhiên, đôi khi, chỉ một phần (vì vậy bạn có thể kết thúc là biết về std::string, nhưng không phải về các chức năng không phải là thành viên được liên kết với nó, như operator<<). Nếu không có nhìn thấy thực tế của bạn bao gồm, tuy nhiên, rất khó để nói.

  2. Trình biên dịch chỉ có thể thực hiện độ phân giải quá tải trên các chức năng , không phải trên mẫu chức năng. Trước khi bắt đầu quá tải độ phân giải , nó sẽ cố gắng tạo nhanh mẫu chức năng có tên chính xác là . Kết quả instantiation thành công trong một hàm, nó thêm vào tập quá tải. Nhưng làm thế nào là nó nghĩa vụ phải nhanh chóng mẫu chức năng của bạn. Nó không có cách nào biết cách sử dụng cho T. Vì vậy, nó không instantiate (mẫu đối số khấu trừ không thành công), và không có trường hợp của nó tìm thấy theo cách của nó vào bộ quá tải.

  3. Tôi không thấy bất kỳ điều gì ngay lập tức tại đây. Sau khi thêm trả về bị thiếu trong số operator<<, nó biên dịch và chạy chính xác trên VC++.

+0

Tôi đang sử dụng std; và tôi có sự bao gồm thích hợp. Tuy nhiên nó là lẻ rằng nó làm việc với VC cho bạn. Nó vẫn không dành cho tôi. Phiên bản thứ ba thích hợp. –

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