2017-02-13 15 views
6

chcę zrobić prostą rzecz:Jak używać lambda jako argumentu szablonu z wartością domyślną w C++?

void DoUntil(auto predicate = [] { return false; }); 

Oczywiście to nie zadziała - trzeba użyć argumentu szablonu:

template <typename P> 
void DoUntil(P predicate = [] { return false; }); 

Ale stwierdzenie to nie działa albo - w Clang daje błąd:

error: no matching function for call to …
note: candidate template ignored: couldn't infer template argument 'P'

Jeśli robię wywołać funkcję bez argumentów, jakoś kompilator powiedzie wywnioskować typ z domyślnym argumentem:

int main() { DoUntil(); } 

Nie chcę w żaden sposób używać std::function<>.

Czy są jakieś inne możliwe rozwiązania mojego problemu?

+1

To nie wydedukowany kontekst. Zapytano o [cppreference] (http: //en.cppreference.com/w/cpp/language/template_argument_deduction) nie wywnioskowany kontekst, Parametr szablonu używany w parametrze type parametru funkcji, który ma domyślny argument, który jest używany w wywołaniu, dla którego wykonywane jest odliczanie argumentów. – felix

+0

Dlaczego nie chcesz użyć 'std :: function'? –

Odpowiedz

6

Użyj funkcji przeciążania zamiast domyślnej funkcji argumentu. Tworzenie non-template funkcję, która pobiera żadnych argumentów oprócz funkcji szablonu:

void DoUntil() ; 

template <typename P> 
void DoUntil(P predicate) ; 

Wersja bez argument może po prostu wywołać wersję szablonu lambda, którego chcesz użyć jako domyślny orzecznika:

void DoUntil() { DoUntil([] { return false; }); } 

problem z oryginalnego podejścia jest to, że starasz się zapewnić domyślny szablonu specjalizacji przez określenie VALU domyślne argumentów e, ale bez określenia domyślnego szablonu typu szablonu:. Nawet bez angażowania lambdy dodaje nie będzie działać, ponieważ T nie posiada domyślny typ, choć t ma wartość domyślną:

template <typename T> 
void Foo(T t = 3); 

Co potrzebne jest, aby określić typ domyślny dla T użyciu <typename T = int>.

Jak napisano w odpowiedzi WhiZTiM, domyślny typ dla sprawy zawierającej funkcję lambda musi zostać wywnioskowany za pomocą decltype. Jest tak, ponieważ lambdy mają unikalne typy znane tylko kompilatorowi.

3

Lambda jest typem anonimowym bez domyślnego konstruktora (z przyczyny można użyć konstruktora kopiowania/przenoszenia, jeśli jest dostępny). Jeśli trzeba przejść drogę lambda, można zrobić:

namespace detail{ auto predicate = [] { return false; }; } 

template <typename P = decltype(detail::predicate)> 
void DoUntil(P pred = detail::predicate); 

Zamiast próbować bawić się wokół z lambdas. Można iść stary dobry sposób:

namespace detail{ 
    struct DefaultPredicate{ bool operator()() const { return false; } }; 
} 

template <typename P = detail::DefaultPredicate> 
void DoUntil(P predicate = P{}); 

Albo jeszcze lepiej, jak Kyle Strand answered.

+0

Jeśli zamierzasz umieścić lambdę w zasięgu przestrzeni nazw, ustaw 'const', aby stała się niejawnie statyczna. – ildjarn

+0

Ah, zastanawiałem się, czy podanie domyślnego argumentu zadziałałoby, gdyby argument-szablon był poprawnie ustawiony domyślnie. Doskonały. –

Powiązane problemy