2012-04-22 17 views
7

Trong chương trình C++ STL sau, tôi định nghĩa hàm functor Nth và nó trả về true nếu nó bị thu hồi trong lần thứ n.Và tôi chuyển nó thành thuật toán chung remove_if, lạ.Chương trình C++ STL sử dụng hàm functor làm vị ngữ

Mã:

#include <iostream> 
#include <list> 
#include <algorithm> 
#include "print.hpp" 

using namespace std; 

class Nth{ 
private: 
    int nth,ncount; 
public: 
    Nth(int n):nth(n),ncount(0){} 

    bool operator()(int) 
    { 
     return ++ncount == nth; 
    } 
}; 

int main() 
{ 
    list<int> col; 
    for (int i = 1;i <=9 ;++i) 
    { 
     col.push_back(i); 
    } 

    PRINT_ELEMENTS(col,"col : "); 

    list<int>::iterator pos; 
    pos = remove_if(col.begin(),col.end(), 
     Nth(3)); 

    col.erase(pos,col.end()); 

    PRINT_ELEMENTS(col,"nth removed : "); 
} 

print.hpp:

#include <iostream> 

template <class T> 
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="") 
{ 
    typename T::const_iterator pos; 

    std::cout << optcstr; 
    for (pos=coll.begin(); pos!=coll.end(); ++pos) { 
     std::cout << *pos << ' '; 
    } 
    std::cout << std::endl; 
} 

tôi chạy nó trong Microsoft Visual Studio 2008 và tôi nhận được kết quả: enter image description here Nó xóa các yếu tố 3 và 6 mà không phải là tôi muốn. Tôi nghĩ rằng chỉ có 3 sẽ bị xóa. Ai đó có thể giải thích cho tôi? Cảm ơn rất nhiều.

Trả lời

11

Từ C++ Thư viện Tiêu chuẩn: A Hướng dẫn và tham khảo By Nicolai M. Josuttis

Điều này xảy ra bởi vì việc thực hiện thông thường của các bản sao thuật toán vị nội bộ trong các thuật toán:

template <class ForwIter, class Predicate> 
    ForwIter std::remove_if(ForwIter beg, ForwIter end, 
          Predicate op) 
    { 
     beg = find_if(beg, end, op); 
     if (beg == end) { 
      return beg; 
     } 
     else { 
     ForwIter next = beg; 
      return remove_copy_if(++next, end, beg, op); 
     } 
    } 

Việc sử dụng thuật toán find_if() để tìm phần tử đầu tiên cần xóa. Tuy nhiên, nó sau đó sử dụng một bản sao của biến vị ngữ đã qua để xử lý các phần tử còn lại nếu có. Ở đây, Nth trong trạng thái ban đầu của nó được sử dụng một lần nữa và nó cũng loại bỏ phần tử thứ ba của các phần tử còn lại, đó là thực tế là phần tử thứ sáu.

Hành vi này không phải là lỗi. Tiêu chuẩn không chỉ định tần suất một vị từ có thể được sao chép nội bộ bằng một thuật toán. Vì vậy, để có được hành vi được bảo đảm của thư viện chuẩn C++, bạn không nên truyền một đối tượng hàm mà hành vi này phụ thuộc vào tần suất nó được sao chép hoặc gọi. Do đó, nếu bạn gọi một vị từ đơn vị cho hai đối số và cả hai đối số đều bằng nhau, thì vị từ phải luôn mang lại kết quả tương tự. Đó là, một vị từ không nên thay đổi trạng thái của nó do một cuộc gọi, và một bản sao của một vị ngữ nên có cùng một trạng thái như bản gốc. Để đảm bảo rằng bạn không thể thay đổi trạng thái của một vị từ do một cuộc gọi hàm, bạn nên khai báo toán tử() như là hàm thành viên liên tục.

+1

Để chính xác hơn, những gì mà OP mong muốn đạt được vẫn có thể thực hiện được. Trạng thái phải được chuyển sang bên ngoài và được chuyển đến vị từ dưới dạng tham chiếu có thể thay đổi. Sau đó tất cả các bản sao của một vị từ đã cho sẽ chia sẻ cùng một trạng thái có thể thay đổi so với bản gốc. –

5

Không sử dụng std::remove_if trên std::list. Thay vào đó, sử dụng chức năng thành viên của danh sách:

col.remove_if(Nth(3)); 

Thuật toán chung sắp xếp lại các giá trị của các yếu tố để bạn có thể xóa một cách an toàn từ cuối cùng, nhưng đối với một danh sách, thuật toán viên loại bỏ các nút không mong muốn trực tiếp mà không cần chạm bất kỳ yếu tố nào khác.

Cập nhật. Như đã được chỉ ra, điều này không thực sự được đảm bảo để giải quyết vấn đề của bạn, vì vị từ của bạn không được phép có trạng thái giá trị nội bộ. Hãy thử thay vào đó:

struct Nth 
{ 
    const int n; 
    int & counter; 
    Nth(int N, int & c) : n(N), counter(c) { } 
    bool operator()(int) const { return ++counter == N; } 
}; 

{ 
    int counter = 0; 
    cols.remove_if(Nth(3, counter)); 
} 

Vị từ mới này có thể sao chép và hoạt động như một trình bao bọc tham chiếu quanh biến đếm bên ngoài (bên ngoài) của bạn.

+0

Điều đó không đảm bảo chỉ sử dụng một bản sao của vị từ, bất kỳ hơn 'std :: remove_if' nào. Nếu nó xuất hiện để sửa chữa vấn đề này, sau đó nó chỉ là do tai nạn. –

+0

@MikeSeymour: Vâng, bạn nói đúng. Tôi sẽ thêm một ghi chú. –

0

Tôi đọc "Tiêu chuẩn C++ Library", và tôi tìm solution.That khác là: tái thực hiện các chức năng remove_if:

template <class ForwIter,class Predicate> 
ForwIter remove_if_re(ForwIter begin,ForwIter end,Predicate op) 
{ 
    while(begin != end && !op(*begin)) 
     ++begin; 
    if(begin == end) 
     return begin; 
    else{ 
     ForwIter next = begin; 
     return remove_copy_if(++next,end,begin,op); 
    } 
} 

Nó không làm việc.

Nhưng tôi hơi tò mò. Thực hiện việc này không sử dụng bản sao của biến vị ngữ đã qua để xử lý các phần tử còn lại ???

Tôi mới học STL.I sẽ đánh giá cao câu trả lời cho bệnh nhân của bạn.

Thanks a lot.

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