2016-03-06 1 views
3

В контексте, где должен быть выведен тип результата вызова функции, C++, похоже, более рад нам помочь, предоставляя (по крайней мере, насколько мне известно следующие два решения):О различных способах получения типа результата функции

  • result of типа черта:

    std::result_of<F(Args...)>::type 
    
  • синтаксис ядра языка:

    decltype(std::declval<F>()(std::declval<Args>()...); 
    

Мой вопрос: Есть ли разница между двумя? Есть ли контекст, в котором нельзя заменить другого, и если бы не нам нужна черта типа, чтобы сделать что-то, что язык мог сделать из коробки?

+0

На самом деле, я не уверен, что это боян. [Этот] (http://stackoverflow.com/q/2689709/2069064) в целом относится к 'decltype'. Это специально в контексте вызова функции. – Barry

ответ

3

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

Таким образом, ваши варианты эквивалентны, если F гарантированно является типом функционального объекта. В противном случае result_of<…> может быть действительным в тех случаях, в которых decltype(…) не является:

struct A {int i;}; 
decltype(std::declval<int A::*>()(std::declval<A>())) // error 
std::result_of_t<int A::*(A)> // = int&&, treated as a member access 

Demo. (Обратите внимание, что result_of является SFINAE дружественным с C++ 14, то есть недопустимые типы вызывают плавный дедукционный сбой, как для decltype.)

1

Возьмите некоторых программистов на С ++, которые не очень опытные, и попросите их угадать, что каждый из фрагменты кода.

Сколько у вас есть первое право? Сколько у кого второе право?

Давая эту операцию, имя делает ее более читаемой.

Теперь возьмите несколько более опытных программистов на С ++ и попросите их получить тип результата вызова функции с использованием и без использования result_of. Сколько их правильно исправить и сколько времени это занимает?

Это уже нетривиальное метапрограммирование, и с его инкапсулированием облегчается поиск с помощью Google и становится все легче.

Кроме того, первая версия немного короче.

Так что результат очень полезен, и поскольку стандартная библиотека сама по себе нуждается в нем довольно много раз, делая его доступным для программиста, тоже тривиально.

4

Существует три отличия.

  1. Первоначально std::result_of не требовалось быть SFINAE удобно.Так что если бы использовать его в контексте, чтобы убедиться, что F был вызван с Args..., это даст вам жесткую ошибку, тогда как decltype с std::declval просто вызовет вероятный сбой замещения. N3462 исправил спецификацию, чтобы сделать ее SFINAE-дружественной.

    На совместимом компиляторе C++ 14 оба являются дружественными SFINAE. std::result_of_t<Fn(ArgsTypes...)> фактически определен в терминах последнего. Из [meta.trans.other]:

    Если выражение INVOKE (declval<Fn>(), declval<ArgTypes>()...) хорошо образуется, когда рассматриваются как невычисленный операнд (раздел 5), типа ЬурейеГо члена имени типа decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); в противном случае, не должно быть ни одного члена .

  2. Как ясно на языке определения result_of, тип Fn может быть что угодно INVOKE -able, в то время как явного вызова std::declval<F>() работает только на функциях и функциональных объектов. Так что, если у вас:

    using F = decltype(&X::bar); 
    using T1 = std::result_of_t<F(X*)>;      // ok 
    using T2 = decltype(std::declval<F>()(std::declval<X*>()); // not ok 
    
  3. Для некоторых типов std::result_of фактически не работает из-за его незаконным, чтобы указать определенный Type-ID с (см my related question). Эти типы используют функции для F (в отличие от указателей/ссылок на функцию) или с использованием абстрактных классов для любого из Args.... Так считают что-то, казалось бы, безобидные, как:

    template <class F, class R = std::result_of_t<F()>> 
    R call(F& f) { return f(); } 
    
    int foo(); 
    call(foo); // error, unresolved overload etc. 
    

    Это терпит неудачу SFINAE (по крайней мере, это не трудно ошибка), потому что мы должны сформировать тип int()() - это нульарной функцию, возвращающую функцию. Теперь, технически, мы неправильно написали наш код. Должно быть:

    template <class F, class R = std::result_of_t<F&()>> 
    R call_fixed(F& f) { return f(); } 
    

    Это работает. Но если бы мы сделали ту же ошибку с declval, we'd've было хорошо:

    template <class F, class R = decltype(std::declval<F>()())> 
    R call_declval(F& f) { return f(); } 
    
+0

Третий: Если 'F' или один из' Args' является абстрактным, или если 'F' является типом функции,' result_of 'недействителен, но' decltype'/'declval' работает правильно. (Исправление состоит в том, чтобы использовать 'result_of '. –

+0

@TC Почему это? – Barry

+0

@Barry Я считаю, что формирую тип «функция возврата функции» или «функция [взятие абстрактного типа по значению]) абстрактный тип по значению "невозможно. – Columbo