2011-09-30 9 views
5

Próbuję napisać funkcję szablonu, która akceptuje std::function, która zależy od argumentów szablonu. Niestety kompilator nie jest w stanie poprawnie odseparować argumentów od std::function. Oto kilka prostych przykładów Kod:C++ 11 Funkcja szablonu, która przyjmuje funkcję std ::, która zależy od parametrów szablonu

#include <iostream> 
#include <functional> 

using namespace std; 

void DoSomething(unsigned ident, unsigned param) 
{ 
    cout << "DoSomething called, ident = " << ident << ", param = " << param << "\n"; 
} 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, std::function< void (Ident, Param) > op) 
{ 
    op(ident, param); 
} 

int main() 
{ 
    unsigned id(1); 
    unsigned param(1); 

    // The following fails to compile 
    // CallFunc(id, param, DoSomething); 

    // this is ok 
    std::function< void (unsigned, unsigned) > func(DoSomething); 
    CallFunc(id, param, func); 

    return 0; 
} 

Gdybym wywołać szablon z następujących czynności:

CallFunc(id, param, DoSomething); 

otrzymuję następujące błędy:

function-tpl.cpp:25: error: no matching function for call to CallFunc(unsigned int&, unsigned int&, void (&)(unsigned int, unsigned int))

Gdybym jawnie utworzyć std :: funkcja poprawnego typu (lub rzucania go) problem znika:

std::function< void (unsigned, unsigned) > func(DoSomething); 
CallFunc(id, param, func); 

W jaki sposób mogę zakodować to tak, aby wyraźne tymczasowe nie było potrzebne?

Odpowiedz

2

Jeśli używasz szablonów, można uniknąć std::function całkowicie, chyba że z jakiegoś powodu chcesz specjalnie ograniczyć funkcję do przyjmowania std::function:

template < typename Ident, typename Param, typename Func > 
void CallFunc(Ident ident, Param param, Func op) 
{ 
    op(ident, param); 
} 
+0

Czy zapomniałeś usunąć znaki komentarza "//" przed linią przerażającą? Mam GCC4.6.1 i GCC odrzuca wspomnianą linię. –

+0

@litb * D'OH!* Dobrze zauważony. –

0

Możesz wykonać konwersję inline lub użyć bind. Nie jest szczególnie ładna, ale to zadanie:

CallFunc(id, param, std::function<void(unsigned, unsigned)>(DoSomething)); 

CallFunc(id, param, std::bind(DoSomething, std::placeholders::_1, std::placeholders::_2)); 

+0

Std :: bind powoduje następujący błąd: function-tpl.cpp: 37: błąd: brak pasującej funkcji dla wywołania 'CallFunc (unsigned int &, unsigned int &, std :: _ Bind , std :: _ Placeholder <2>)) (unsigned int, unsigned int)>) ' – mark

+0

@mark: Masz rację, 'bind' nie zezwala na odliczanie argumentów, a będziesz musiał powiedzieć' CallFunc < unsigned, unsigned> (...) '. Więc ... po prostu użyj wyraźnej konwersji. –

8

Musisz uczynić trzeci parametr funkcji kontekstem niewynikającym dla parametrów szablonu w nim zawartych. Następnie kompilator nie porówna argumentu z typem parametru bez uwzględnienia wszystkich niejawnych konwersji (Standard mówi, a C++ 0x wyjaśnił to dalej, że dla parametru funkcji, w którym nie ma żadnych parametrów szablonu w dedukcji pozycji, wszystkie niejawne konwersje są dozwolone w pomniejszaniu różnicy).

template < typename T > struct id { typedef T type; }; 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       typename id<std::function< void (Ident, Param) >>::type op) 
{ 
    op(ident, param); 
} 

Zamiast id można użyć boost::identity. W C++ 0x i kompilatory, które go obsługują, można mieć bardziej czytelny edycję przy użyciu szablonów alias

template < typename T > using nondeduced = typename id<T>::type; 

Następnie kod staje się po prostu

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       std::function< nondeduced<void (Ident, Param)> > op) 
{ 
    op(ident, param); 
} 

Jednakże GCC nie obsługuje jeszcze szablony aliasu.

+0

Już bym pomyślał o parametrze w nie wywnioskowanym kontekście. Dobrze to wiedzieć! –

+0

Jak to się stało, że pierwsza wersja zawija cały typ funkcji, ale druga wersja tylko opakowuje parametr szablonu? –

+0

@KerrekSB z powodu różnych interpretacji w kompilatorach brzmienia w C++ 03, przyszedłem, aby zawinąć cały typ parametru w nie wywnioskowany kontekst. Niektóre kompilatory wybierałyby najlepsze części tego typu przed wejściem do niewydecyzowanego kontekstu (tj. 'Std :: function ' parts) i porównują to z argumentami, powodując niepowodzenie dedukcji . Inne kompilatory by tego nie robiły. Jeśli owijamy cały typ, nie pozostawiają "nietkniętych" części, więc to wszystko jest "..." (czarna dziura) i nie mogą wystąpić żadne niedopasowania. Zobacz http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1184 –

Powiązane problemy