2012-08-20 2 views
9

Этот вопрос является продолжением How to deduce the type of the functor's return value? Я переформулирую его более абстрактным образом.Общий способ вывода возвращаемого типа функтора?

Учитывая псевдокод шаблонной функции

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

где <ret-expr> произвольное выражение, которое включает в себя arg, что я буду использовать для <decl-expr>, чтобы установить тип возвращаемого ComputeSomething равного типа возвращаемого функтора.

Функтор может быть классом, лямбдой или указателем функции.

Частичные решения, которые я нашел до сих пор.

(a) Ответ на мой связанный вопрос, сделанный ecatmur. По существу, он повторяет оператор возврата в <decl-expr>. Проблемы: он подвержен ошибкам и не работает, если содержит локальные переменные.

(б) работает только для указателей на функции

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

(с) предполагает, что аргумент функтора типа Arg (который не может держать в целом) и требует Arg быть default- построимо

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

(д) Использование std::declval, который, как предполагается, чтобы снять ограничение по умолчанию-конструктивны, как это было предложено в how to deduce the return type of a function in template. Может ли кто-нибудь объяснить, как это работает?

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

Мне жаль это говорить, но AFAIK это не представляется возможным, потому что возвращение трейлинг не видит шаблон функции определяется, в то время как body делает. –

ответ

4

Вот мое собственное решение, лучшее, что я мог бы получить

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

result_of. Он обратно совместим и берет всю уродливую боль declval из вашего кода. Вам все равно нужно помнить, чтобы добавить квалификаторы ссылочных значений rvalue (&&), если вы на самом деле просто пересылаете значения.

Что-то еще я считаю важным: ваша функция переводит аргументы в другую функцию. В таких случаях вы всегда должны использовать ссылки rvalue для передачи аргументов.

Если все, что вы пытаетесь сделать, это улучшить ремонтопригодность: есть несколько попыток макроса RETURNS, которые пытаются свести к минимуму повторение между объявлением типа возвращаемого значения и фактическим возвратным выражением, но я не видел ни одного, что позволяет тело функции, содержащее больше, чем фактический оператор return.

Как работает declval: его компилятор зависит. Не допускается встречаться в оцениваемом содержимом, и его аргумент может быть неполным. См 20.2.4

+0

Я бы посоветовал против 'std :: result_of'. На данный момент это не гарантируется SFINAE, в отличие от решений на основе 'decltype'. Он также не отражает тип результата вызова - он делает больше, чем это, например, в отношении указателей на членов. –

+0

@LucDanton Я всегда воспринимал «делать больше» в качестве выгоды. Обработка случая указателей на функции-члены ужасно утомительна, и 'result_of' решает эту проблему. Можете ли вы подробнее рассказать о проблеме SFINAE? – pmr

+0

Нет смысла в 'std :: result_of' вычислять тип результата, с которым шаблон функции не справляется -' f (a, b, c) 'является синтаксической ошибкой, когда' f' является указателем на элемент. Что касается SFINAE, рассмотрите перегруженный 'apply', так что' apply (f, args ...) 'либо приводит к' f (args ...) ', либо, если он не сформирован, применяется к' f (* args ...) '(допустим для простоты, чтобы эти два выражения никогда не были хорошо сформированы). SFINAE необходимо удалить либо перегрузку для успешного вызова. –

2

Вариация (б) работать не только с указателями на функции должны быть чем-то вроде

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

Для того, чтобы (с) работает для всего, что вам нужно 2 перегрузкам. Первых, как показано в (с), второй:

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

Кроме того, как показывает gcc bug 54111 - вывод типа возвращаемого значения является очень ненадежным.

11

std::declval - это шаблон функции, который только объявлен (не определен). Таким образом, он может использоваться только в неоцененных контекстах, таких как аргумент sizeof и decltype. Он объявляется как возвращающее значение r указанного типа. Это позволяет использовать его для создания фиктивного параметра для вызова функции в выражении decltype.

например.

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

t заявляет, что тип результата вызова fn с RValue типа Arg. Это похоже на ваш случай (c) (fn(Arg())), но он не требует ничего от Arg, поэтому он работает с типами без конструкторов по умолчанию.

Если ваше выражение return использует локальную переменную типа foo, то вы можете использовать decltype(fn(std::declval<foo>())), снова независимо от того, как вы строите foo.

Если вам нужно lvalue, например, названный объект или ссылка на lvalue, вы можете использовать std::declval<foo&>(). Это позволяет обрабатывать случай, когда тип зависит от того, есть ли значение lvalue или rvalue.

+2

'std :: declval ()' также следует учитывать, особенно если учесть локальную переменную. –

Смежные вопросы