2013-06-27 32 views
10

Đây là một sự kỳ quặc, nơi tôi không biết, nếu nó là với tiêu chuẩn C++, với trình biên dịch của tôi (G ++ phiên bản 4.6.3 trên Ubuntu 12.04, là dài hạn mới nhất hỗ trợ phiên bản của Ubuntu) hoặc với tôi, những người không hiểu ;-)std :: hoán đổi lạ với G ++

các mã trong câu hỏi là đơn giản như sau:

#include <algorithm> // for std::swap 
void f(void) 
{ 
    class MyClass { }; 
    MyClass aa, bb; 
    std::swap(aa, bb);   // doesn't compile 
} 

Khi cố gắng để biên dịch với G ++, trình biên dịch mang lại như sau thông báo lỗi:

test.cpp: In function ‘void f()’: 
test.cpp:6:21: error: no matching function for call to ‘swap(f()::MyClass&, f()::MyClass&)’ 
test.cpp:6:21: note: candidates are: 
/usr/include/c++/4.6/bits/move.h:122:5: note: template<class _Tp> void std::swap(_Tp&, _Tp&) 
/usr/include/c++/4.6/bits/move.h:136:5: note: template<class _Tp, long unsigned int _Nm> void std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm]) 

Kết quả đáng ngạc nhiên là, mà chỉ cần di chuyển các định nghĩa lớp ra khỏi chức năng làm cho mã biên dịch tốt:

#include <algorithm> // for std::swap 
class MyClass { }; 
void f(void) 
{ 
    MyClass aa, bb; 
    std::swap(aa, bb);   // compiles fine! 
} 

Vậy là nó, đó std :: swap() là không được phép làm việc trên lớp, đó là riêng tư cho các chức năng? Hoặc đây có phải là lỗi với G ++, có thể là phiên bản cụ thể của G ++ tôi đang sử dụng không?

Thậm chí khó hiểu là, rằng sau đây không hoạt động trở lại, mặc dù MyListClass cũng là tư nhân (nhưng kéo dài một lớp "chính thức", mà có thể là một thực hiện cụ thể của swap() tồn tại) hơn:

#include <algorithm>  // for std::swap 
#include <list>    // for std::list 
void g(void) 
{ 
    class MyListClass : public std::list<int> { }; 
    MyListClass aa, bb; 
    std::swap(aa, bb);    // compiles fine! 
} 

Nhưng chỉ cần thay đổi từ các đối tượng thành con trỏ và biên dịch lại không thành công:

#include <algorithm>  // for std::swap 
#include <list>    // for std::list 
void g(void) 
{ 
    class MyListClass : public std::list<int> { }; 
    MyListClass aa, bb; 
    MyListClass* aap = &aa; 
    MyListClass* bbp = &bb; 
    std::swap(aap, bbp); // doesn't compile! 
} 

Tất nhiên, trong ứng dụng thực tế, các lớp học phức tạp hơn; Tôi đã đơn giản hóa mã càng nhiều càng tốt để vẫn tái tạo vấn đề.

Trả lời

16

Nếu bạn đang chạy trong chế độ C++ 03, mà tôi cho là như vậy, bạn không được phép sử dụng loại được xác định cục bộ trong mẫu. Nếu đây là trường hợp bạn có thể xác định loại của bạn ở cấp độ không gian tên để làm cho nó hoạt động, hoặc người nào khác bạn có thể biên dịch trong chế độ C++ 11, nơi nó cần biên dịch. [*]

Trong trường hợp bạn tự hỏi tại sao trường hợp thứ hai hoạt động, tiêu chuẩn không cung cấp chuyên môn của

template <typename T> void swap(T&,T&) // [1] 

std::list là bản thân mẫu và bạn không thể chuyên về chức năng mẫu. Những gì nó cung cấp là một mẫu cơ sở khác nhau:

template <typename T, typename A> void swap(list<T,A>&,list<T,A>&); // [2] 

Như trong trường hợp trước, trình biên dịch không thể sử dụng loại địa phương của bạn với [1], do đó sẽ bị loại bỏ. Sau đó, nó cố gắng [2], và nó thấy rằng nó có thể chuyển đổi giá trị của kiểu cục bộ thành tham chiếu đến cơ sở std::list<int>, và sau đó chuyển đổi [2] là một ứng cử viên tốt. Sau đó, nó sẽ gọi số

std::swap(static_cast<std::list<int&>>(aa),static_cast<std::list<int&>>(bb)); 

không sử dụng loại cục bộ, mà đúng hơn là không gian tên std::list<int>.

Mặt khác, thực tế là nó biên dịch không có nghĩa là nó làm những gì bạn muốn. Cụ thể, nếu loại mở rộng MyListClass thêm bất kỳ biến thành viên mới nào, những biến này sẽ không được đổi thành.

Tất cả những gì được nói, và cũng giống như một lưu ý phụ: bạn không nên kế thừa từ các thùng chứa tiêu chuẩn vì chúng không bao giờ được thiết kế để được kế thừa từ đó.

[*] Tuyên bố từ chối trách nhiệm: Tôi không biết tính năng này có được hỗ trợ trong phiên bản trình biên dịch cụ thể đó không, bạn sẽ phải kiểm tra kỹ.

+1

Theo [Apache wiki] (http://wiki.apache.org/stdcxx/C%2B%2B0xCompilerSupport), GCC hỗ trợ điều này từ 4.5, do đó, sẽ đủ để thêm tùy chọn '-std = c + + 0x'. – Angew

+0

Cảm ơn bạn rất nhiều vì câu trả lời của bạn! Thêm -std = C++ 0x làm cho mọi thứ biên dịch tốt ngay cả với các lớp địa phương. Wow, tôi không biết về sự không tương thích với các lớp và các mẫu cục bộ trong các tiêu chuẩn C++ cũ hơn. –

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