Різниця між std :: result_of та decltype


100

У мене є проблеми з розумінням потреби std::result_ofв C ++ 0x. Якщо я правильно зрозумів, result_ofвикористовується для отримання отриманого типу виклику об'єкта функції з певними типами параметрів. Наприклад:

template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
    return f(a);
}

Я не бачу різниці в наступному коді:

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
    return f(a);
}

або

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
    return f(a);
}

Єдина проблема, яку я можу побачити з цими двома рішеннями, полягає в тому, що нам потрібно:

  • мати примірник функтора, щоб використовувати його у виразі, переданому до decltype.
  • знати визначений конструктор для функтора.

Правильно чи я думати , що єдина відмінність між decltypeі result_ofв тому , що першим потрібно вираз , тоді як другий не робить?

Відповіді:


86

result_ofбула введена в Boost , а потім включена в TR1 і, нарешті, в C ++ 0x. Тому result_ofє перевага, яка є сумісною назад (із відповідною бібліотекою).

decltype є абсолютно новою річчю в C ++ 0x, не обмежує лише повернення типу функції і є мовною особливістю.


У будь-якому випадку, gcc 4.5 result_ofреалізується в умовах decltype:

  template<typename _Signature>
    class result_of;

  template<typename _Functor, typename... _ArgTypes>
    struct result_of<_Functor(_ArgTypes...)>
    {
      typedef
        decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
        type;
    };

4
Наскільки я розумію, decltypeце гірше, але й потужніше. result_ofможе використовуватися лише для типів, які можна викликати, і для цього потрібні типи як аргументи. Наприклад, ви не можете використовувати result_ofтут: template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );якщо аргументи можуть бути арифметичними типами (немає такої функції F, яку можна визначити F(T,U)представляти t+u. Для визначених користувачем типів ви могли б. Так само (я не дуже грав з цим) що дзвінки до методів учасників важко обійтися result_ofбез використання в'яжучих чи лямбда
David Rodríguez - dribeas

2
Одне зауваження, decltype потребує аргументів, щоб викликати функцію з AFAIK, тому без результату_of <> незручно повертати тип, що повертається шаблоном, не покладаючись на аргументи, що мають дійсні конструктори за замовчуванням.
Роберт Мейсон

3
@RobertMason: Ці аргументи можна отримати, використовуючи std::declval, як код, який я показав вище. Звичайно, це некрасиво :)
kennytm

1
@ DavidRodríguez-dribeas У вашому коментарі є не закриті дужки, які відкриваються з "(є" :(
Navin

1
Ще одне зауваження: result_ofі його тип помічників result_of_tзастарілий, як C ++ 17, на користь, invoke_resultа також invoke_result_tзменшує деякі обмеження. Вони вказані внизу en.cppreference.com/w/cpp/types/result_of .
сигма

11

Якщо вам потрібен тип чогось, що не є чимось на зразок виклику функції, std::result_ofпросто не застосовується. decltype()може дати вам тип будь-якого виразу.

Якщо ми обмежимось лише різними способами визначення типу повернення виклику функції (між std::result_of_t<F(Args...)>і decltype(std::declval<F>()(std::declval<Args>()...)), то є різниця.

std::result_of<F(Args...) визначається як:

Якщо вираз INVOKE (declval<Fn>(), declval<ArgTypes>()...)добре сформований, коли трактується як неоцінений операнд (п. 5), тип typededef тип повинен decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); інакше назвати тип , не повинен бути тип члена.

Різниця між result_of<F(Args..)>::typeі decltype(std::declval<F>()(std::declval<Args>()...)все в цьому полягає INVOKE. Використання declval/ decltypeбезпосередньо, окрім того, що вводиться дещо довше, є дійсним лише за умови Fпрямого виклику (тип об’єкта функції або функція або покажчик функції). result_ofдодатково підтримує покажчики на функції членів та покажчики на дані членів.

Спочатку використання declval/ decltypeгарантоване SFINAE-дружнє вираження, тоді як std::result_ofможе призвести до вашої помилки замість відрахування. Це було виправлено в C ++ 14: std::result_ofтепер потрібно бути SFINAE-дружнім (завдяки цій роботі ).

Отже, на відповідному компіляторі C ++ 14 std::result_of_t<F(Args...)>суворо перевершує. Це чіткіше, коротше і правильно підтримує більше Fs .


Якщо тільки ви не використовуєте його в контексті, коли ви не хочете дозволити вказівникам членам, то це std::result_of_tбуде досягати успіху у випадку, коли ви, можливо, хочете, щоб він не вийшов.

За винятками. Хоча він підтримує покажчики на членів, result_ofне буде працювати, якщо ви спробуєте інстанціювати недійсний ідентифікатор типу . До них можна віднести функцію, яка повертає функцію або приймає абстрактні типи за значенням. Наприклад:

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

int answer() { return 42; }

call(answer); // nope

Правильне використання було б result_of_t<F&()>, але це деталь, яку вам не потрібно пам’ятати decltype.


Для нереференційного типу Tта функції template<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }, чи result_of_tправильне використання ?
Zizheng Tai

Крім того, якщо аргумент, який ми передаємо, f- це const T, чи слід його використовувати result_of_t<F&&(const T&)>?
Zizheng Tai
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.