Каковы некоторые виды использования decltype (auto)?

В С++ 14 введена идиома decltype(auto).

Обычно его использование заключается в разрешать объявления auto использовать правила decltype для данного выражения.

Ищем примеры "хорошего" использования идиомы, я могу думать только о вещах вроде следующего (Scott Meyers), а именно для вывода типа возвращаемого типа:

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

Есть ли другие примеры, в которых эта новая языковая функция полезна?

Ответ 1

Пересылка типа возвращаемого типа в общем коде

Для не общего кода, как и в предыдущем примере, вы можете вручную выбрать, чтобы получить ссылку как возвращаемый тип:

auto const& Example(int const& i) 
{ 
    return i; 
}

но в общем коде вы хотите иметь возможность отлично перенаправлять тип возврата, не зная, имеете ли вы дело со ссылкой или значением. decltype(auto) дает вам эту способность:

template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}

Отложенный возврат типа возврата в рекурсивных шаблонах

В этом Q & A несколько дней назад была обнаружена бесконечная рекурсия во время создания шаблона, когда возвращаемый тип шаблона был указанный как decltype(iter(Int<i-1>{})) вместо decltype(auto).

template<int i> 
struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) 
{ return iter(Int<i-1>{}); }

int main() { decltype(iter(Int<10>{})) a; }

decltype(auto) используется здесь для задержки вывода возвращаемого типа после того, как пыль шаблона была установлена.

Другие использования

Вы также можете использовать decltype(auto) в других контекстах, например. в проекте стандарта N3936 также указано

7.1.6.4 автоопределение [dcl.spec.auto]

1 Спецификации типа auto и decltype(auto) обозначают местозаполнитель тип, который будет заменен позже, либо путем вычитания из инициализатор или явным определением с типом возвращаемого типа. Тип типа auto также используется для обозначения того, что лямбда является общая лямбда.

2 Тип заполнителя может отображаться с помощью декларатора функции в описании-specifier-seq, type-specifier-seq, convert-function-id или trailing-return-type, в любом контексте, где такой декларатор действителен. Если функция declarator включает в себя тип trailing-return-type (8.3.5), который задает объявленный тип возвращаемого значения функции. Если объявленный тип возвращаемой функции содержит тип заполнителя, возвращаемый тип функции выводится из операторов возврата в теле функции, если таковая имеется.

Проект также содержит этот пример инициализации переменной:

int i;
int&& f();
auto x3a = i;                  // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto x4a = (i);                // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto x5a = f();                // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto x6a = { 1, 2 };           // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i;                // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)

Ответ 2

Цитата из здесь:

  • decltype(auto) в первую очередь полезен для вывода возвращаемого типа функций пересылки и подобных оболочек, где вы хотите, чтобы тип точно отслеживал какое-то выражение, вызываемое вами.

  • Например, учитывая следующие функции:


   string  lookup1();
   string& lookup2();

  • В С++ 11 мы могли бы написать следующие функции-оболочки, которые не забывают сохранить ссылочную форму возвращаемого типа:

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }

  • В С++ 14 мы можем автоматизировать это:

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }

  • Однако decltype(auto) не является широко используемой функцией, кроме этого.

  • В частности, хотя он может использоваться для объявления локальных переменных, то это, вероятно, просто антипаттерн, поскольку ссылка на локальные переменные не должна зависеть от выражения инициализации.

  • Кроме того, он чувствителен к тому, как вы пишете оператор return.

  • Например, две приведенные ниже функции имеют разные типы возврата:


   decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
   decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }

  • Первый возвращает string, второй возвращает string&, который является ссылкой на локальную переменную str.

Из предложения вы можете увидеть больше предполагаемых применений.