2012-12-10 8 views
6

Wiem, że to pytanie zostało zadane wcześniej, ale pomimo tego, że jestem dość doświadczonym programistą, nie rozumiem odpowiedzi i nie widzę sposobu, aby odpowiedzieć na te poprzednie pytania. wyjaśnienie. Nie ma linku "odpowiedź" ani nic. Poza tym te pytania były dość stare. Tak, zadaję to pytanie na nowo.Jak przeładować sygnaturę std :: function w C++

Mam klasę, w której przeciążam operatora + =. Chcę jeden przeciążenie wziąć gołe wskaźnik funkcji, a drugi do podjęcia std :: funkcję:

void operator+=(void (*handler)()); 
void operator+=(function<void (void *, T)> handler); 

Zastosowanie:

MyClass a; 
a += [](){ DoSomething(); }; 
a += [](void *x, T y){ DoSomething(); }; 

Niestety kod nie skompilować, ponieważ kompilator nie może określić że drugie przeciążenie nie jest odpowiednie dla pierwszego połączenia + =.

Jak zdefiniować operatora + = funkcje członka, aby rozwiązać ten problem? Nie chcę zmieniać sposobu używania operatorów (np. Za pomocą wyraźnego rzutowania). Chcę, żeby działały, jak wykazano powyżej.

Ponadto byłoby mieć pod ręką następujące przeciążenia, a także:

void operator+=(function<void()> handler); 

Ale znowu, nie może spowodować przeciążenie na podstawie podpisu funkcji <> Szablon.

Zobacz ten wątek do dalszych przykładów: Isn't the template argument (the signature) of std::function part of its type? (próbowałem wdrażania różnych rozwiązań, o których mowa w tym wątku, a żaden z nich nie skompilować)

byłem programowania przez wiele lat w licznych języków, ale moje umiejętności w C++ są trochę zardzewiałe.

+0

** duże ** pytanie brzmi: __ co zamierzasz zrobić z danym wskaźnikiem funkcji lub 'std :: function' obiektem w twoim przeciążeniu' operator + = '? __ – zaufi

+0

@zaufi: Nie widzę jak to jest istotne, ale w porządku. Zamierzam dodać go do std :: vector funkcji obsługi zdarzeń. Zasadniczo wdrażam zdarzenia .NET w C++. Ponieważ Moce, które Nadal nie zaprzątały sobie głowy faktem, że Wydarzenia i Właściwości są - jak Zamknięcia - absolutnie niezbędną funkcjonalnością dla nowoczesnego języka programowania. –

+0

, więc musisz mieć 'std :: vector' utworzony z' std :: function' z exacly 1 (sugeruję 'void()') podpis ... Mam rację? – zaufi

Odpowiedz

3

Następujący kod działa dobrze z g ++ 4.7.2:

#include <functional> 
#include <iostream> 

template <typename T> 
typename std::enable_if<std::is_convertible<T, void(*)()>::value>::type 
foo(T&&) 
{ 
    std::cout << "foo(void(*)())" << std::endl; 
} 

void foo(std::function<void(void*,int)>) 
{ 
    std::cout << "foo(std::function<void(void*,int)>)" << std::endl; 
} 

int main() 
{ 
    foo([]{}); 
    foo([](void*,int){}); 
} 

Problem spowodowany jest przez konstruktora std::function, która została zgłoszona jako template <class F> function(F);. Ten konstruktor akceptuje wszystko jako argument. Jeśli argument ten nie ma odpowiedniej wartości operator(), podczas generowania konstruktora generowany jest błąd.

Niestety proces rozwiązywania przeciążenia nie wymaga tworzenia żadnych funkcji szablonów, więc kompilator uważa, że ​​wszystko można przekonwertować na dowolny typ std::function.

+0

Dzięki, właśnie udało mi się dostać to do mojej klasy bez problemów; chociaż rzeczy były trochę owłosione, gdy próbowałem rzucić T na funkcję <>. Wygląda na to, że najpierw muszę go rzucić w pustkę (*)(). Twoje zdrowie. –

+0

Myślę, że moim głównym problemem jest to, że nigdy nie słyszałem o enable_if i nie rozumiem tego. Miałem niejasną świadomość, że może to pomóc (patrząc na odpowiedzi na podobne pytania), ale nie wiedziałem, w jaki sposób został użyty. Nadal nie jestem pewien, jak to działa. –

+0

Niektóre informacje o 'enable_if' można znaleźć [tutaj] (http://www.boost.org/doc/libs/1_52_0/libs/utility/enable_if.html) i [tutaj] (http: //en.cppreference .com/w/cpp/types/enable_if). – hpsMouse

0

Ok, jeśli masz std::vector<std::function<void()>>, nie musisz nawet mieć zbyt wielu przeciążeń. Najprostszym sposobem jest zdefiniowanie na szablonie operator+=, możliwego "strzeżonego" przy pomocy std::enable_if, aby można go było włączyć tylko w przypadku wymienialnych obiektów lub wskaźników funkcji. Tak więc przechodzące lambdy lub surowe wskaźniki wykonają automatyczną konwersję dla ciebie podczas przypisywania (push_back/emplace_back). Przekazywanie std::function zostanie po prostu przypisane zgodnie z oczekiwaniami.

+0

Brakuje ci punktu. Chcę dodać zarówno "[]() {}" lambdas i "[] (void *, T) {}" lambdas do mojego wektora. Zawijam je we wspólnej klasie kontenerów i dodajemy to do mojego wektora. Próba przekonania mnie, że nie potrzebuję różnych podpisów lambda to marnowanie czasu i mojego czasu. –

Powiązane problemy