Які існують функції decltype (auto)?


151

В c ++ 14 decltype(auto)вводиться ідіома.

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

Шукаючи приклади «хорошого» використання ідіоми, я можу лише думати такі речі, як наступне (від Скотта Майєрса ), а саме для вирахування повернення типу функції :

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

Чи є інші приклади, коли ця нова мова корисна?


2
цей пост в основному передбачає , щоб спробувати уникнути цієї ідіоми , тому що при його використанні ви даєте менше варіантів для оптимізації для вашого компілятора stackoverflow.com/a/20092875/2485710
user2485710

Я колись використовував decltype(auto)щось подібне template<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; }, хоч потім зрозумів, що треба використовувати те, return (p.first);що дивно працює (але IIRC це навіть призначено).
dyp

@ user2485710 не впевнений, що йдеться саме про оптимізацію, тим більше потенціал аварій, якщо decltype(auto)щось може скопіювати / перенести в оголошений об’єкт, що суперечить очікуванням.
підкреслюй_

Відповіді:


170

Переадресація повернення типу в загальному коді

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

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)...); 
}

Затримка вирахування типу повернення в рекурсивних шаблонах

У цьому запитанні кілька днів тому було виявлено нескінченну рекурсію під час описування шаблону, коли тип повернення шаблону було вказано 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 Тип заповнювача заповнення може з'являтися разом із декларатором функції у decl-specier-seq, type-specier-seq, id-перетворення-функції або id-типу, що повертається, у будь-якому контексті, де такий декларатор дійсний . Якщо декларатор функцій включає тип зворотного повернення (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)

17
Чи відрізняється поведінка (i)проти iнової речі в С ++ 14?
Данвіл

14
@Danvil, decltype(expr)і decltype((expr))вони вже відрізняються від C ++ 11, це узагальнює таку поведінку.
TemplateRex

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

Прикладом, який завжди викликає огиду, є синтаксис однофайлового файлу до рядка (також згадуваний у цьому посиланні). Кожна його частина здається відсталою. Ви можете зовсім не чекати двозначності і примусово видаляти зайві дужки із зразка; Ви очікували б неоднозначності вирішити шляхом усунення відповідно до SFINAE, але потенційні кандидати, окрім декларації, усуваються заздалегідь (SF - AE); і в розчаруванні ви можете рухатися далі, як тільки він збирає думки, що довільні парени вирішують двозначність, але вони вводять її. Я найбільше хвилююся для професорів CS101.
Іван П

@TemplateRex: Про затримку вирішення типу повернення у згаданому питанні: Наскільки я бачу, у конкретному сценарії простий autoвиконав би цю роботу так само добре, як результат все одно повертається за значенням ... Або я пропустив щось?
Аконкагуа

36

Цитуючи тут матеріали :

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

  • Наприклад, з урахуванням наведених нижче функцій:


   string  lookup1();
   string& lookup2();

  • В C ++ 11 ми могли б записати наступні функції обгортки, які запам'ятовують для збереження опорного типу повернення:

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

  • У C ++ 14 ми можемо автоматизувати, що:

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

  • Однак decltype(auto)це не є широко використовуваною функцією поза цим.

  • Зокрема, хоча це може бути використано для декларування локальних змінних , але це, мабуть, просто антипатерн, оскільки посилання на локальну змінну не повинно залежати від виразу ініціалізації.

  • Крім того, це чутливо до того, як ви пишете зворотну заяву.

  • Наприклад, дві функції нижче мають різні типи повернення:


   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.

З пропозиції можна побачити більше використання.


3
Чому б просто не використовувати autoдля повернення?
BЈоviћ

@ BЈовић також може працювати з узагальненим відрахуванням типу повернення (тобто autoповерненням), але ОП запитує спеціально для використання decltype(auto).
101010

3
Але питання все ще актуальне. Яким буде тип повернення auto lookup_a_string() { ... } ? Це завжди нереференційний тип? І тому auto lookup_a_string() ->decltype(auto) { ... }потрібна сила, щоб дозволити повернення посилань (в деяких випадках)?
Аарон Макдейд

@AaronMcDaid Deductible autoвизначається терміном проходження шаблону значення, тому так, це не може бути посиланням. Будь ласка, зачекайте auto, звичайно, будь- що, включаючи посилання.
curiousguy

4
Додатковим прикладом, який варто згадати, є повернення елемента a std::vector. Скажіть, у вас є template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }. Тоді S<bool>::operator[]повернеться звисаючі посилання через спеціалізацію std::vector<bool>. Зміна типу повернення, щоб decltype(auto)обійти цю проблему.
Xoph
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.