2011-10-19 31 views
9

std::forward_list cung cấp insert_aftererase_after thành viên có thể không cần truy cập thực tế đối tượng std::forward_list. Do đó, chúng có thể được thực hiện như các hàm thành viên static và được gọi mà không có đối tượng danh sách - hữu ích cho một đối tượng muốn xóa chính nó khỏi danh sách, đây là một cách sử dụng rất phổ biến. EDIT: Tối ưu hóa này chỉ áp dụng cho forward_list các chuyên môn trên std::allocator hoặc các trình phân bổ không trạng thái do người dùng xác định.Có thể std :: thành viên forward_list được triển khai tĩnh không?

Có thể triển khai tuân thủ tiêu chuẩn thực hiện việc này không?

§17.6.5.5/3 nói

A call to a member function signature described in the C++ standard library behaves as if the implementation declares no additional member function signatures.

với một chú thích

A valid C++ program always calls the expected library member function, or one with equivalent behavior. An implementation may also define additional member functions that would otherwise not be called by a valid C++ program.

Nó không rõ ràng với tôi cho dù thêm static sẽ tạo ra một "khác biệt" hàm thành viên, nhưng loại bỏ một (implicit) đối số không nên phá vỡ bất cứ điều gì mà thêm đối số mặc định sẽ không, và đó là hợp pháp. (Bạn không thể lấy PTMF một cách hợp pháp đối với bất kỳ chức năng thành viên tiêu chuẩn nào.)

Tôi cho rằng thư viện phải được phép thực hiện việc này, nhưng tôi không chắc liệu một số quy tắc có bị hỏng hay không. Và các quy tắc chức năng thành viên được liệt kê như thế nào?

+3

Thao tác danh sách đột biến yêu cầu quyền truy cập vào cấp phát của danh sách, vì vậy tôi nghi ngờ chúng có thể là tĩnh (đặc biệt là với các trình phân bổ trạng thái mới). –

+0

Tuy nhiên, mẫu có thể được chuyên biệt cho trường hợp cực kỳ phổ biến của 'std :: allocator' và tương tự như vậy bởi người dùng cho riêng mình nếu muốn. – Potatoswatter

+0

Làm thế nào bạn sẽ chuyên về điều này dựa trên kiến ​​thức về * iterator * một mình? Trình lặp không biết danh sách thuộc về danh sách nào, cũng không phải danh sách cấp phát nào sử dụng. –

Trả lời

9

Tiêu chuẩn cho biết bạn có thể thoát khỏi nó nếu không ai có thể biết sự khác biệt. Và bạn chính xác là người ta không thể tạo ra một PTMF hợp pháp thành forward_list, vì vậy bạn an toàn theo cách đó.

Sự nguy hiểm của trình phân bổ tùy chỉnh đã được chỉ ra. Nhưng ngay cả đối với std::allocator<T>, vẫn có nguy cơ ai đó có thể chuyên std::allocator<MyType> và sau đó phát hiện ra rằng allocator::construct/destroy không được gọi.

Được rồi, nhưng một chuyên gia có thể nói là std::forward_list<int> (không có người phân bổ tùy chỉnh, không có giá trị được xác định bởi người dùng) và làm cho insert_after tĩnh?

Không. Thay đổi này có thể phát hiện được với các khả năng mới của SFINAE. Đây là một bản demo:

#include <memory> 
#include <iostream> 

template <class T, class A = std::allocator<T>> 
class forward_list 
{ 
public: 
    typedef T value_type; 
    struct const_iterator {}; 
    struct iterator {}; 

    iterator insert_after(const_iterator p, const T& x); 
}; 

template <class C> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    -> decltype(C::insert_after(p, x)) 
{ 
    std::cout << "static\n"; 
    return typename C::iterator(); 
} 

template <class C> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    -> decltype(c.insert_after(p, x)) 
{ 
    std::cout << "not static\n"; 
    return typename C::iterator(); 
} 

int main() 
{ 
    ::forward_list<int> c; 
    test(c, ::forward_list<int>::const_iterator(), 0); 
} 

Chương trình này chạy và in ra:

not static 

Nhưng nếu tôi làm insert_after tĩnh:

static iterator insert_after(const_iterator p, const T& x); 

Sau đó, tôi nhận được một lỗi thời gian biên dịch:

test.cpp:34:5: error: call to 'test' is ambiguous 
    test(c, ::forward_list<int>::const_iterator(), 0); 
    ^~~~ 
test.cpp:16:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >] 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    ^
test.cpp:24:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >] 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    ^
1 error generated. 

Phát hiện sự khác biệt.

Do đó, không phù hợp để làm cho forward_list::insert_after tĩnh.

Cập nhật

Nếu bạn muốn làm cho "tĩnh" quá tải callable, bạn chỉ cần làm cho nó nhẹ hơn mong muốn hơn so với tình trạng quá tải "không tĩnh".Một cách để làm điều đó đang thay đổi "không tĩnh" quá tải để:

template <class C, class ...Args> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x, Args...) 
    -> decltype(c.insert_after(p, x)) 
{ 
    std::cout << "not static\n"; 
    return typename C::iterator(); 
} 

Bây giờ kiểm tra sẽ in ra hoặc là "tĩnh" hoặc "không tĩnh" tùy thuộc vào việc các chức năng insert_after thành viên là tĩnh hay không.

+0

Đã không nghĩ về người dùng chuyên biệt 'std :: allocator', điểm tuyệt vời. Nhưng không phải là "" tĩnh "' quá tải một mẫu mà không có chuyên môn tốt có thể hình thành, do đó hình thành bệnh không cần chẩn đoán? – Potatoswatter

+0

Cập nhật câu trả lời với đầy đủ chức năng "máy dò tĩnh". –

+0

Được rồi ... không khó, nhưng sự khác biệt này như thế nào, ví dụ, phát hiện một đối số mặc định bằng cách tìm loại của một hàm thành viên không quá tải? Bây giờ khuôn mẫu tạo ra một chuyên môn hợp lệ, nhưng nó vẫn có vẻ là UB vì biểu thức 'C :: insert_after()' không được định nghĩa bởi tiêu chuẩn. Tương tự như vậy, việc triển khai thực hiện theo khuôn mẫu của Phiên bản "Allocator :: construct' không được tuân thủ trong C++ 03 vì nó có thể được gọi là' construct <> (...) '? Đây thực sự là câu trả lời của câu hỏi ban đầu, theo chiều sâu của đặc tả thư viện. – Potatoswatter

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