2012-08-20 15 views
9

To pytanie jest kontynuacją How to deduce the type of the functor's return value? Przeformułowuję je w bardziej abstrakcyjny sposób.Ogólny sposób na dedukcję typu zwrotu funktora?

Biorąc pod uwagę Pseudokod z funkcji szablonu

template <typename Arg, typename Fn> 
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>) 
{ 
// do something 
// ............ 
return fn(<ret-expr>) 
} 

gdzie <ret-expr> jest arbitralne wyrażenie, które wiąże arg, co będę używał do <decl-expr> aby ustawić typ zwracanej ComputeSomething równą typu powrotnej funktora.

Funktor może być klasą, wskaźnikiem lambda lub funkcją.

Częściowe rozwiązania, które znalazłem do tej pory.

(a) Odpowiedź na moje powiązane pytanie wykonane przez ecatmur. Zasadniczo jest to powtórzenie oświadczenia zwrotu w <decl-expr>. Problemy: jest podatny na błędy i nie działałby, gdyby zawierał zmienne lokalne.

(b) działa tylko wskaźników funkcji

template <typename Arg, typename Ret> 
Ret ComputeSomething(Arg arg, Ret(*fn)(Arg)) 

(C), to zakłada się, że argument funktora jest typu Arg (które mogą posiadać ogólnie) i wymaga Arg być Default constructible

template <typename Arg, typename Fn> 
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg()) 

(d) Korzystanie std::declval która ma podnieść domyślną-constructible ograniczenie, jak zasugerowano w how to deduce the return type of a function in template. Czy ktokolwiek mógłby wyjaśnić, jak to działa?

template <typename Arg, typename Fn> 
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(std::declval<Arg>()) 
+0

przykro mi to mówić, ale AFAIK nie jest to możliwe, ponieważ powrót spływu nie zobaczyć szablon funkcja jest określona, ​​natomiast ciało to robi. –

Odpowiedz

4

Oto moje własne rozwiązanie, najlepiej mogę dostać

template <typename Arg, typename Fn> 
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn) 
10

Użyj result_of. Jest kompatybilny wstecz i usuwa z kodu wszystkie brzydkie declval. Nadal musisz pamiętać o dodaniu kwalifikatorów referencyjnych rvalue (&&), jeśli faktycznie po prostu przekazujesz wartości.

Coś jeszcze uważam za ważne: Twoja funkcja przekazuje argumenty do innej funkcji. W takich przypadkach należy zawsze używać odwołań rvalue do przekazywania argumentów.

Jeśli wszystko, co próbujesz zrobić, to poprawić łatwość konserwacji: jest kilka prób na makrze RETURNS, które starają się zminimalizować powtórzenia między deklaracją zwrotną a faktycznym zwrotem, ale nie widziałem, aby to było możliwe treść funkcji zawierająca więcej niż faktyczna instrukcja return.

Co do sposobu działania declval: zależy od kompilatora. Niedozwolone jest występowanie w ocenianej zawartości, a jej argument może być niekompletny. Zobacz 20.2.4

+0

Odradzam 'std :: result_of'. Po raz pierwszy nie ma gwarancji SFINAE, w przeciwieństwie do rozwiązań opartych na "decltype". Nie odzwierciedla też typu wyniku połączenia - robi więcej, np. w odniesieniu do wskaźników do członków. –

+0

@LucDanton Zawsze postrzegałem "robić więcej" jako korzyść. Posługiwanie się przypadkiem wskaźników funkcjami członkowskimi jest potwornie uciążliwe, a 'result_of' rozwiązuje ten problem. Czy możesz bardziej szczegółowo opisać problem SFINAE? – pmr

+0

Nie ma sensu w 'std :: result_of' obliczania typu wyniku, którego szablon funkcji nie może obsłużyć -' f (a, b, c) 'jest błędem składni, gdy' f' jest wskaźnikiem do elementu. Jeśli chodzi o SFINAE, rozważ przeładowane 'apply', aby' apply (f, args ...) 'albo powodowało' f (args ...) 'albo, jeśli nie jest dobrze uformowane, ucieka się do' f ' (* argumentuje ...) '(załóżmy dla uproszczenia, że ​​te dwa wyrażenia nigdy nie są jednocześnie dobrze uformowane). Aby odnieść sukces, potrzebujesz SFINAE, aby usunąć przeciążenie. –

2

wariant (B) pracuje nie tylko ze wskaźników funkcji powinno być coś jak

template<typename Arg, typename Ret> 
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f) 
3

Aby uczynić (c) działa na wszystko, czego potrzebujesz 2 przeciążeń. 1-ta, jak pokazano w punkcie (c), 2:

template <typename Arg, typename Ret> 
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn) 

Ponadto, jak pokazano gcc bug 54111 - dedukcja typu powrotnej jest bardzo zmienna.

11

std::declval to szablon funkcji, który jest tylko zadeklarowany (niezdefiniowany). Dlatego może być używany tylko w nienazwanych kontekstach, takich jak argumenty pod numerami sizeof i decltype. Zadeklarowano, że zwraca wartość rwartości określonego typu. Dzięki temu można go użyć do wytworzenia fałszywego parametru dla wywołania funkcji w wyrażeniu decltype.

np.

typedef decltype(fn(std::declval<Arg>())) t; 

t deklaruje się być typem wyniku wywołania fn z rvalue typu Arg. Jest to podobne do twojego przypadku (c) (fn(Arg())), ale nie wymaga niczego od Arg, więc działa na typach bez domyślnych konstruktorów.

Jeśli w wyrażeniu zwrotnym używana jest zmienna lokalna typu foo, można ponownie użyć decltype(fn(std::declval<foo>())), niezależnie od tego, jak konstruujesz obiekt foo.

Jeśli potrzebujesz lwartości, na przykład nazwanego obiektu lub odniesienia lwartości, możesz użyć std::declval<foo&>(). Pozwala to na obsługę przypadku, w którym typ zależy od tego, czy masz wartość l, czy rwartość.

+2

'std :: declval ()' jest również czymś, co należy wziąć pod uwagę, szczególnie biorąc pod uwagę wymienianie zmiennej lokalnej. –

Powiązane problemy