2017-01-09 12 views
8

Mam następujący typ cechę:Jak mogę znaleźć liczbę argumentów funkcji szablonu?

template <class T> 
struct Arity : Arity<decltype(&T::operator())> {}; 

template <class T, class R, class... Args> 
struct Arity<R(T::*)(Args...)> { 
    static constexpr auto value = sizeof...(Args); 
}; 

template <class T, class R, class... Args> 
struct Arity<R(T::*)(Args...) const> { 
    static constexpr auto value = sizeof...(Args); 
}; 

template <class R, class... Args> 
struct Arity<R(*)(Args...)> { 
    static constexpr auto value = sizeof...(Args); 
}; 

który działa wspaniale jest znaleźć liczbę argumentów funkcji potrzebny większości przypadków użycia, ale to nie dla jednej wspólnej sprawy:

auto l1 = [](int, double){}; 
Arity<decltype(l1)>::value; // works, 2 

auto l2 = [](auto, auto){}; 
Arity<decltype(l2)>::value; // error: Reference to overloaded function could not be resolved; did you mean to call it? 

wierzę nie da się na ogół wykonać tej pracy dla dowolnej funkcji/operatora(), ponieważ w zależności od typów/wartości przekazywanych jako typy szablonów, można wybrać inne przeciążenie, lub może w ogóle nie być dostępne przeciążenie. Ponadto, nie ma sposobu, aby wiedzieć, jakie poprawne typy i wartości przekazać jako argumenty szablonu. Ale nadal, chcę, żeby to działało dla wspólnego przypadku argumentów lambda pobierających auto. Czy jest jakiś sposób, aby uczynić to bardziej niezawodnym i obejmować lambdy, które biorą auto argumenty?

+0

Czy awaria kompilacja widać ciężko awaria lub SFINAE powodując żadnych kwalifikowanych kandydatów? Myślę, że istnieje sposób na policzenie argumentów funkcji, ale nie jestem pewien, czy ładnie wybrałby między bieżącą a wersją z obsługą szablonu lambda. –

+0

@BenVoigt 'Odniesienie do przeciążonej funkcji nie mogło zostać rozwiązane; czy chciałeś to nazwać? ' – David

+0

Dobrze, więc masz poważny błąd. To sprawia, że ​​trochę bardziej bolesne jest tworzenie rozwiązania, które działa zarówno z argumentami typu "i bez szablonu" na 'operator()'. –

Odpowiedz

4

Podejrzewam, że osiągnąłem tutaj połowę rozwiązania. Działa tylko do określonej liczby parametrów, ale w przypadku większości aplikacji nie powinno to stanowić problemu. Co więcej, jest to prawdopodobnie wysoce uproszczone, ale mój mózg nie jest teraz podstępnym SFINAE.

template < 
    class, std::size_t N, 
    class = std::make_index_sequence<N>, 
    class = void_t<> 
> 
struct CanCall : std::false_type { }; 

template <class F, std::size_t N, std::size_t... Idx> 
struct CanCall< 
    F, N, 
    std::index_sequence<Idx...>, 
    void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))> 
> : std::true_type { }; 

CanCall<F, N> powróci czy F jest wywoływalny z N parametrów dowolnego typu. Typ pomocnika Any ma niewymienionych operatorów konwersji, które umożliwiają przekształcenie go w dowolny pożądany typ parametru.

template <class F, std::size_t N = 0u, class = void> 
struct Arity : Arity<F, N + 1u, void> { }; 

template <class F, std::size_t N> 
struct Arity<F, N, std::enable_if_t<CanCall<F, N>::value>> 
: std::integral_constant<std::size_t, N> { }; 

template <class F> 
struct Arity<F, MAX_ARITY_PROBING, void> 
: std::integral_constant<std::size_t, ARITY_VARIADIC> { }; 

Arity<F> tylko sprawdza, czy F można nazwać z zero, jeden, dwa ... parametrów. Pierwsze pozytywne czekanie wygrywa. Jeśli osiągniemy parametry MAX_ARITY_PROBING, to wygaśnie Arity i przypuszcza, że ​​funkcja jest albo wariantowa, albo w ogóle nie jest funkcją.

See it live on Coliru

+0

O mój. Niesamowity. '[] (auto &) {}' łamie je przez – David

+0

@David naprawiono! – Quentin

+0

Teraz nie działa z '[] (int &&) {}' – David

2

Nie sądzę, że można użyć funkcji lambda w przypadku użycia, którego typem argumentów jest auto. Funkcje takich funkcji lambda są najprawdopodobniej zaimplementowane za pomocą szablonów funkcji.

Stąd decltype nie może być stosowany z:

auto l2 = [](auto, auto){}; 
Arity<decltype(l2)>::value; 

this answer to another SO question Zobacz więcej na ten temat.

+0

ya Byłem całkiem pewien, że to była odpowiedź - to po prostu niemożliwe. – David

+0

Jest to raczej komentarz niż odpowiedź, pokazująca, że ​​jedno podejście nie działa, jest dalekie od udowodnienia, że ​​nie istnieje żadne podejście do pracy. –

+0

Sądzę też, że źle to analizujesz. 'decltype (l2)' jest w porządku, to '& T :: operator()' wewnątrz 'Arity' który zawodzi. 'l2' nie jest szablonem (jak przyjęto nietestowaną sugestię), raczej jest klasą bez szablonu, która ma szablon' operator() '. –

Powiązane problemy