2015-01-28 10 views
6

Có thể sử dụng std::unique với các trình vòng lặp được tạo bằng phương tiện của hàm std::make_move_iterator không? Tôi đã thử the following và đã thành công:Thuật toán duy nhất với các trình dịch chuyển di chuyển

#include <iostream> 
#include <ostream> 
#include <vector> 
#include <algorithm> 
#include <limits> 
#include <iterator> 

#include <cstdlib> 

struct A 
{ 

    A() : i(std::numeric_limits<double>::quiet_NaN()) { std::cout << __PRETTY_FUNCTION__ << "\n"; } 
    A(double ii) : i(ii) { std::cout << __PRETTY_FUNCTION__ << "\n"; } 
    A(A const & a) : i(a.i) { std::cout << __PRETTY_FUNCTION__ << "\n"; } 
    A(A && a) : i(std::move(a.i)) { std::cout << __PRETTY_FUNCTION__ << "\n"; a.i = std::numeric_limits<double>::quiet_NaN(); } 
    A & operator = (A const & a) { std::cout << __PRETTY_FUNCTION__ << "\n"; i = a.i; return *this; } 
    A & operator = (A && a) { std::cout << __PRETTY_FUNCTION__ << "\n"; i = std::move(a.i); a.i = std::numeric_limits<double>::quiet_NaN(); return *this; } 
    bool operator < (A const & a) const { std::cout << __PRETTY_FUNCTION__ << "\n"; return (i < a.i); } 
#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wfloat-equal" 
    bool operator == (A const & a) const { std::cout << __PRETTY_FUNCTION__ << "\n"; return (i == a.i); } 
#pragma clang diagnostic pop 

    friend 
    std::ostream & 
    operator << (std::ostream & o, A const & a) 
    { 
     return o << a.i; 
    } 

private : 

    double i; 

}; 

int 
main() 
{ 
    std::vector<A> v{1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 5.0, 6.0, 6.0, 7.0}; 
    std::cout << "v constructed\n\n\n\n"; 
    std::sort(v.begin(), v.end()); 
    auto const end = std::unique(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end())).base(); 
    std::copy(v.begin(), end, std::ostream_iterator<A>(std::cout, " ")); 
    std::cout << std::endl; 
    return EXIT_SUCCESS; 
} 

Nhưng có thể thành công phụ thuộc vào triển khai?

Và điều gì liên quan đến các thuật toán khác từ <numeric><algorithm>?

+0

'std :: unique' vẫn di chuyển, vì vậy tôi không thấy những gì bạn thu được từ nó. –

+0

@ T.C. Ý anh là gì? 'std :: unique' copy-assigning [ở đây] (http://en.cppreference.com/w/cpp/algorithm/unique) trong phần" Có thể triển khai ". – Orient

+1

Đó không phải là một triển khai tốt. Thư viện thực [di chuyển] (http://coliru.stacked-crooked.com/a/198b772ffc2f41ba). –

Trả lời

5

Chương trình được đảm bảo hoạt động theo tiêu chuẩn.
std::unique yêu cầu trình lặp vòng chuyển tiếp. Cách dễ nhất để hiển thị lên rằng lặp di chuyển đáp ứng yêu cầu đó là để kiểm tra việc iterator_category typedef của move_iterator:

typedef typename iterator_traits<Iterator>::iterator_category iterator_category; 

Như bạn có thể thấy, các loại iterator của các loại iterator cơ bản là trực tiếp chuyển thể. Trong thực tế, hành vi của vòng lặp Động thái này là gần như tương đương với những người cơ bản của họ, [move.iterators]/1:

Lớp mẫu move_iterator là một adapter iterator với cùng hành vi như iterator cơ bản ngoại trừ việc gián tiếp của nó Toán tử chuyển đổi hoàn toàn giá trị được trả về bởi toán tử gián tiếp của toán tử bên dưới của biến lặp thành tham chiếu rvalue.

Không có yêu cầu đáng chú ý nào khác: Rõ ràng vector<>::iterator là trình lặp đầu vào (theo yêu cầu của [move.iter.requirements]). Chỉ yêu cầu có liên quan áp đặt bởi chính nó là unique

Loại *first phải đáp ứng các yêu cầu MoveAssignable (Bảng 22).

... được đáp ứng đơn giản.

Lưu ý rằng việc sử dụng các trình vòng lặp di chuyển sẽ không mang lại lợi thế nào so với các trình duyệt thông thường. Nội bộ các phần tử trùng lặp được di chuyển-gán (do đó yêu cầu MoveAssignable), do đó, trả lại một tham chiếu rvalue từ operator* là thừa.

+0

Không, nội bộ 'std :: unique' phải sử dụng chuyển nhượng trực tiếp. Sử dụng 'swap' cũng sẽ yêu cầu' MoveConstructible'. –

+0

@ T.C. Dường như là chính xác. Các phần tử có thực sự được hoán đổi sau đó hay là các phần tử sau khi kết thúc mới được trả lại trong trạng thái chuyển từ trạng thái chuyển? – Columbo

+0

@ T.C. libstdC++ dường như làm '* ++ __ dest = _GLIBCXX_MOVE (* __ first)', điều này chỉ ra rằng cái sau là đúng. – Columbo

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