2016-04-29 21 views
5

Chức năng sau đây áp dụng một functor trên mỗi phần tử và làm giảm giá trị trả về:Iterator-giảm mẫu cho các chức năng có thể có hoặc không có thể trả về một giá trị

template <class FCT, class RED> 
RED::TYPE forAllElements(FCT functor, RED reducer){ 
    for(/* all elem in elements */){ 
    reducer(functor(elem)); 
    } 
    return reducer.value; 
} 

Bây giờ, thỉnh thoảng tôi có thể muốn chỉ cần gọi functor trên tất cả các yếu tố, và không làm giảm bất cứ điều gì. Về cơ bản, tôi thì muốn có cái gì đó như:

class FunctorThatReturnsNothing{ 
    void operator() (Elem e){ 
    // do something, return nothing... 
    } 
} 

class DummyReducer{ 
    using TYPE = void; // ??? something like that ??? 

    template <class FCT> 
    void operator() (/* ??? what here */){ 
    // do nothing... 
    } 
} 

forAllElements(FunctorThatReturnsNothing(), DummyReducer()); 

Nhưng điều đó sẽ không biên dịch kể từ khi tôi có reducer(functor(elem)) nơi giá trị trả về không tồn tại của một hàm khoảng trống được thực hiện như là một cuộc tranh cãi.

Có cách nào để làm cho nó hoạt động cho functors void mà không nhân đôi choAllElements cho một void và một trường hợp không void?

(Đối với những người nghi ngờ XY-Problem: về cơ bản tôi biết các cách tiếp cận khác nhau để lặp và giảm và tôi nghĩ rằng cách tiếp cận gọi lại được trình bày phù hợp với trường hợp của tôi. Tôi tự hỏi làm cách nào để tránh mã trùng lặp "return value + reduce" và trường hợp "just callback".)

+0

Vì vậy, về cơ bản bạn muốn [ 'std :: for_each'] (http://en.cppreference.com/w/ cpp/algorithm/for_each)? –

+0

@JoachimPileborg, xem phần cuối cùng của câu hỏi, trong ngoặc đơn. Tôi biết rằng tôi có thể đi con đường đó với vòng lặp, nhưng trong tình hình hiện tại, một giải pháp gọi lại như được trình bày là thuận lợi. – Michael

+1

Cách tiếp cận sạch sẽ nhất là 'return reducer' thay vì' return reducer.value'. Bạn có thể cấu trúc lại mã của bạn để sử dụng nó không? – filipos

Trả lời

1

Tôi nghĩ bạn chỉ có thể thực hiện một lớp học VoidReducer, nhưng thay vì return reducer.value; bạn sẽ cần return reducer.getvalue();. Sau đó, bạn chỉ cần thực hiện void VoidReducer::getvalue(){}.

Tôi chưa thử nghiệm điều này nhưng ý tưởng sẽ hoạt động. Bạn được phép return f(); nếu cả hai f và chức năng hiện tại có kiểu trả về void.

EDIT
Bây giờ tôi đọc những câu hỏi một cách cẩn thận hơn, tôi thấy rằng vấn đề bạn đang hỏi về là dòng reducer(functor(elem));.
Đối với điều này tôi sẽ biên dịch thời gian công văn dựa trên decltype(functor(elem)).

template <class Functor, class Reducer, class Elem> 
void Combine(Functor functor, Reducer & reducer, Elem elem, std::true_type) 
{ 
    functor(elem); 
} 

template <class Functor, class Reducer, class Elem> 
void Combine(Functor functor, Reducer & reducer, Elem elem, std::false_type) 
{ 
    reducer(functor(elem)); 
} 

template <class Functor, class Reducer, class Elem> 
void Combine(Functor functor, Reducer & reducer, Elem elem) 
{ 
    Combine(functor, reducer, elem, std::is_same<decltype(functor(elem)), void>()); 
} 

Sau đó gọi Combine thay vì reducer(functor(elem)) sẽ giảm một cách chính xác giá trị trả về của functor khi và chỉ khi nó không phải là vô hiệu.

PS: Rắc tài liệu tham khảo và std::forward gọi để nếm thử.

1

Nếu bạn vẫn muốn sử dụng chức năng forAllElements của bạn thay vì nếu sử dụng std::for_each trực tiếp bạn có thể tạo ra một tình trạng quá tải của các chức năng mà không phải mất một giảm, và chỉ đơn giản là sử dụng std::for_each nội bộ:

template <class FCT> 
void forAllElements(FCT functor){ 
    std::for_each(std::begin(...), std::end(...), functor); 
} 

... 

forAllElements(FunctorThatReturnsNothing()); 

Nếu nó không phải có thể nhận được "vòng lặp" cho vùng chứa của bạn (nếu bạn sử dụng vùng chứa hoặc con trỏ không chuẩn), bạn có thể có vòng lặp của riêng mình và chỉ cần gọi functor.

0

Cách tiếp cận sạch sẽ và linh hoạt là trả về chính đối tượng hàm, thay vì trường value được mã hóa cứng.

template <class FCT, class RED> 
RED forAllElements(FCT functor, RED reducer){ 
    for(/* all elem in elements */){ 
    reducer(functor(elem)); 
    } 
    return reducer; 
} 

Thao tác này có thể không có giá trị trả lại hoặc thậm chí nhiều hơn một giá trị trả về, với tên thông tin.

phương pháp ban đầu của bạn có thể được xem như là một wrapper tiện qua thành phần nguyên thủy này:

template <class FCT, class RED> 
RED::TYPE forAllElements_convenience(FCT functor, RED reducer){ 
    return forAllElements(functor, reducer).value; 
} 
Các vấn đề liên quan