6

Tôi có hai đoạn mã cho ADL cho mục đích demo. Cả hai đoạn mã đều được biên soạn bởi VC10, gcc & các trình biên dịch com ++ C++ và kết quả là giống nhau cho cả ba.Tại sao ADL được ưu tiên hơn một hàm trong 'không gian tên std' nhưng bằng chức năng trong không gian tên do người dùng xác định?

< 1> ADL chống sử dụng chỉ thị của một người sử dụng không gian tên được xác định:

#include <algorithm> 
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using M::swap; 
    N::T o1,o2; 
    swap(o1,o2); 
} 

Compile kết quả:

error C2668: 'M::swap' : ambiguous call to overloaded function 
could be 'void M::swap(N::T,N::T)' 
or  'void N::swap(N::T,N::T)' [found using argument-dependent lookup] 

này được dự kiến ​​như ADL không được ưu tiên hơn kết quả tra cứu thông thường cộng với ADL không phải là công dân hạng hai, kết quả tìm kiếm ADL được kết hợp với tra cứu không bình thường (không ADL) bình thường. Đó là lý do tại sao chúng ta có sự mơ hồ.

< 2> ADL chống sử dụng chỉ thị của namespace std:

#include <algorithm> 
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} //point 1 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using std::swap; 
    N::T o1,o2; 
    swap(o1,o2); 
} 

Cái này biên dịch ok.

Kết quả là trình biên dịch chọn kết quả ADL (nó có tiền lệ của std :: swap), nghĩa là N::swap() tại 'điểm 1' sẽ được gọi. Chỉ khi trong 'điểm 1' vắng mặt (giả sử nếu tôi nhận xét ra dòng đó), biên dịch sẽ sử dụng sự sụt giảm trở lại std::swap thay thế.

Lưu ý cách này đã được sử dụng ở nhiều nơi như một cách để ghi đè lên std::swap. Nhưng câu hỏi của tôi là, tại sao ADL được ưu tiên hơn 'không gian tên std' (case2) nhưng được coi là tương đương với hàm không gian tên do người dùng định nghĩa (case1)?

Có đoạn nào trong tiêu chuẩn C++ nói như vậy không?

============================================== =================================== Chỉnh sửa sau khi đọc các câu trả lời hữu ích, có thể hữu ích cho người khác.

Vì vậy, tôi đã tinh chỉnh đoạn mã của mình 1 & giờ sự mơ hồ đã biến mất và biên dịch một cách thích hợp, thích chức năng Nontemplate khi thực hiện quá trình phân giải quá tải!

#include <algorithm> 
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} 
} 

namespace M 
{ 
    template<class T> 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using M::swap; 
    N::T o1,o2; 
    swap(o1,o2); //here compiler choose N::swap() 
} 

Tôi cũng đã tinh chỉnh đoạn trích của mình 2. Chỉ để làm cho sự mơ hồ xuất hiện chỉ để giải trí!

#include <algorithm> 
namespace N 
{ 
    struct T {}; 

    template<class _Ty> inline 
    void swap(_Ty& _Left, _Ty& _Right) 
    { 
     _Ty _Tmp = _Move(_Left); 
     _Left = _Move(_Right); 
     _Right = _Move(_Tmp); 
    } 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using std::swap; 
    N::T o1,o2; 
    swap(o1,o2); 
} 

gcc và comeau cả nói mơ hồ như mong đợi:

"std::swap" matches the argument list, the choices that match are: 
      function template "void N::swap(_Ty &, _Ty &)" 
      function template "void std::swap(_Tp &, _Tp &)" 

BTW VC10 ngu ngốc như thường lệ chúng ta hãy một đường chuyền này ok trừ khi tôi loại bỏ các 'sử dụng std :: swap'.

Chỉ cần thêm một chút để viết: C++ quá tải có thể được khôn lanh (hơn 30 trang trong C++ chuẩn), nhưng ít appendlix B có một rất có thể đọc được 10 trang có ...

Cảm ơn tất cả các đẹp đầu vào, bây giờ nó rõ ràng.

Trả lời

9

Kiểm tra của bạn không kiểm tra xem ADL có được ưu tiên hay không so với tra cứu thông thường, mà là cách độ phân giải quá tải xác định kết quả phù hợp nhất. Lý do mà trường hợp thử nghiệm thứ hai hoạt động là std::swap là một mẫu, và khi thực hiện độ phân giải quá tải trên một kết hợp hoàn hảo (được tìm thấy bởi ADL) và một mẫu, hàm không có khuôn mẫu được ưu tiên.

+0

Điểm đáng lưu ý là ADL liên quan đến * tra cứu tên * và tra cứu tên không có khái niệm "ưu tiên". –

+0

@KerrekSB: Tôi nghĩ rằng David đang nói về sau giai đoạn tra cứu tên trong khi ở độ phân giải quá tải, đó là tất cả về việc chọn trận đấu hay nhất. – Gob00st

12

Một cuộc gọi chức năng sẽ xảy ra trong nhiều giai đoạn :

  1. tra cứu tên -> đặt chức năng ứng cử viên trong một cái gọi là quá tải thiết
    • này là phần mà ADL sẽ xảy ra nếu bạn có tra cứu tên không đủ tiêu chuẩn
  2. khấu trừ đối số mẫu -> cho mỗi mẫu trong tập quá tải
  3. độ phân giải quá tải -> chọn phù hợp nhất

Bạn đang bối rối phần 1 với phần 3. tra cứu tên sẽ thực sự đưa cả swap chức năng trong tập quá tải ({N::swap, std::swap}), nhưng phần 3 sẽ quyết định, mà một để gọi cuối cùng.

Bây giờ, kể từ khi std::swap là một mẫu, và tiêu chuẩn nói rằng chức năng phi mẫu là chuyên biệt hơn hơn chức năng mẫu khi thực hiện giải quyết tình trạng quá tải, <2> cuộc gọi của bạn N::swap:

§13.3.3 [over.match.best] p1

Với các định nghĩa này, một hàm khả thi F1 được định nghĩa là một hàm tốt hơn một hàm khả thi khác F2 nếu [...]

  • F1 là một hàm phi mẫu và F2 là một hàm mẫu chuyên môn [...]

† Tôi khuyên bạn nên ba video đầu tiên của this excellent series về đề tài này.

+0

Vì vậy, nhận xét về downvote xin vui lòng? – Xeo

+0

Cảm ơn bạn đã tham khảo tiêu chuẩn! – Gob00st

+0

@Xeo: dunno (liên quan đến downvote), câu đầu tiên nhầm lẫn tôi một chút, bởi vì trình biên dịch không tìm thấy * tất cả * chức năng có thể được gọi. Tra cứu tên là tất cả về chỉ tìm một tập hợp con. –

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