2016-06-15 19 views
11

byłem pisanie funkcje wyższego rzędu tak:Doskonałe-spedycyjnych wywoływalne obiekty w funkcji wyższego rzędu

template<typename F, typename... Args> 
void doStuff(F f, Args&&... args) 
{ 
    // ... 
    f(std::forward<Args>(args)...); 
    // ... 
} 

A może zastąpić F f z F&& f.

Ale po tym, jak dowiedziałem się o ref-kwalifikatorach (ouch), sprawy się skomplikowały. Wyobraźmy sobie klasę funktora:

struct Foo { 
    void operator()(...) &; 
    void operator()(...) &&; 
}; 

Wtedy moje wcześniejsze realizacje doStuff będzie zawsze tylko wywołać metodę &, ponieważ parametry są zawsze lwartościami.

Myślę, że sposób, aby rozwiązać ten jest wdrożenie doStuff takiego:

template<typename F, typename... Args> 
void doStuff(F&& f, Args&&... args) 
{ 
    // ... 
    std::forward<F>(f)(std::forward<Args>(args)...); 
    // ... 
} 

Jest to również sposób std::result_of jest possibly implemented. Co chcę wiedzieć, czy jest jakaś wada do tej implementacji, tj. Czy powinienem zastąpić wszystkie moje wdrożenia HOF?

+0

Wygląda dobrze, o ile 'f' nie zostanie ponownie użyty po tym. Możliwe, że 'operator()' będzie miał pewne skutki uboczne, które mogą modyfikować wewnętrzne elementy (przesunięcie na wewnętrznych elementach) 'f' (jeśli jakieś). Nie jestem jednak pewien, nie przemyślałem tego. Dobre pytanie. – Arunmu

+0

@Arunmu If (1) metoda jest przeciążona na ref-kwalifikacjach, oraz (2) dzwoniący przekazuje funktor jako rwartość, mamy wystarczające powody, aby sądzić, że dzwoniący zamierza wywołać wersję '&&'. W tym przypadku prawdopodobnie rozsądne jest, aby perfekcyjnie przekazać każde wystąpienie 'f' w' doStuff' ... myślę. –

+1

Nie widzę żadnego problemu z 'forward (f)' tak długo jak nie używaj 'f' ponownie w' doStuff' – Praetorian

Odpowiedz

0

std::forward to warunkowa obsada wartości r, a punktem jest zwykła kradzież wnętrzności umierających obiektów. Nie ma różnicy między functors i innymi obiektami pod tym względem. Wadą jest to samo, co przy każdym użyciu std::forward; jeśli nie jesteś przygotowany na kradzież tych flaków, nie powinieneś.

#include <iostream> 
#include <vector> 

struct Foo { 
    std::vector<int> vec = {1,2,3}; 
    std::vector<int>& operator()(...) & { 
     return vec; 
    } 
    std::vector<int> operator()(...) && { 
     return std::move(vec); 
     // Clearly, don't rely on 'vec' here... 
    } 
}; 

Foo getFoo(){ 
    return {}; 
} 

int main() { 
    auto foo = getFoo(); 
    auto vec1=foo(); 
    auto vec2=getFoo()(); 
} 

Oczywiście, nie rób rzeczy tak:

auto vec3 = std::move(foo)(); 
vec4 = foo(); 

z tych samych powodów.

Powiązane problemy