Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Секреты auto и decltype

Anna | 24.06.2014 | нет комментариев
Новый эталон языка принят касательно давным-давно и теперь теснее, вероятно, нет программиста, тот, что не слышал о новых ключевых словах auto и decltype. Но как примерно с любым аспектом С , применение этих новых инструментов не обходится без нюансов. Некоторые из них я постараюсь осветить в этой статье.

Для разминки предлагаю начать с небольшого теста.

Тест

1. Какой тип будет у переменных ri1..riN позже выполнения дальнейшего кода?

int foo();
int& foo1();
const int foo2();
const int& foo3();

int main()
{
  auto ri = foo();
  auto ri1 = foo1();
  auto ri2 = foo2();
  auto ri3 = foo3();

  auto& ri4 = foo();
  auto& ri5 = foo1();
  auto& ri6 = foo2();
  auto& ri7 = foo3();

  auto&& ri8 = foo();
  auto&& ri9 = foo1();
  auto&& ri10 = foo2();
  auto&& ri11 = foo3();

  int k = 5;
  decltype(k)&& rk = k;

  decltype(foo())&& ri12 = foo();
  decltype(foo1())&& ri13 = foo1();

  int i = 3;
  decltype(i) ri14;
  decltype((i)) ri15;
}

Скомпилируются ли следующие фрагменты?

2. auto lmbd = [](auto i){...};
3. void foo(auto i); 
4. decltype(auto) var = some_expression; //WTF?!
5. auto var = {1, 2, 3}; //Если да, какой тип будет у var?
6. template<typename T> void foo(T t){}
   foo({1, 2, 3});
Теория

К механизму итога типов, используемому в образцах в С 11 добавилось два новых механизма: auto и decltype. И Дабы жизнь программистам не казалась медом, все эти 3 механизма выводят типы по-своему. Механизм, применяемый auto, в точности копирует механизм образцов, за исключением типа std::initializer_list.

auto var = {1, 2, 3};  //  Ok, var будет иметь тип std::initializer_list<int>
template<typename T> void foo(T t);
foo({1, 2, 3}); // Не компилируется

Объяснений такому поведению немножко и все они не отличаются ясностью. Скотт Мейерс, скажем, по этому поводу пишет так: “I have no idea why type deduction for auto and for templates is not identical. If you know, please tell me!”. В С 14 данный механизм менять не собираются. За трактование дозволено попровать принять тот факт, что работают, скажем, такие восхитительные вещи:

template<typename T>
void fill_from_list(T& cont, const T& l);

std::vector<int> v;
fill_from_list(v, {1, 2, 3});
Auto

Выходит, как же `auto` выводит тип? К сожалению, тут нет простого правила на все случаи жизни, помимо, вероятно, того, что `auto` при итоге типа в всеобщем случае отбрасывает cv квалификаторы и ссылки. Ниже я перечислю самые значимые моменты.

1.

auto var = some_expression;

Если тип some_expression T* либо const T*, то тип var также будет T* либо const T* соответственно. Пока без сюрпизов. Дальше — увлекательнее. Вероятно самое значимое с утилитарной точки зрения правило заключается в том, что если тип some_expression — Tconst TT& либо const T&то типом var будет T. Это, однако, если задуматься, абсолютно разумно, чай в этом случае значение, возвращаемое some_expressionкопируется в var и дозволено отважно писать вот так:

void foo(const std::list<widget_t>& l)
{
  auto w = l.front();
  l.pop();
  //  work with `w` here
}

2.

auto& var = some_expression;

В этом случае, ожидаемо, если тип some_expression — T либо const T, компилироваться это не будет, так какlvalue ссылку невозможно инициализировать rvalue. Если тип some_expression — T&, то и var будет иметь типT&. Тут значимым моментом является то, что если тип some_expression — const T&, то и тип var будет const T&.

3.

auto&& var = some_expression;

Тут действует придуманное (либо по крайней мере озвученное) Скоттом Мейерсом правило “универсальных ссылок”. Оно заключается в том, что тип var будет зависеть от того какая value category у some_expression. Если rvalue, то тип var будет T&&, если же lvalue, то T&Cv квалификаторы при этом сохраняются.

Auto как параметр функции

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

auto foo(auto v1, auto v2) -> decltype(v1 v2) ; 
int foo(auto v1, bool v2); 

foo(“C   is cool?”, true);

Впрочем в с 14 дозволено будет применять auto праметры в лямбдах.

decltype

С decltype обстановка с одной стороны труднее (если посмотреть формальные правила), с иной стороны проще (если выделить основные моменты). Я сформулирую эти правила так, как я их осознал.
Выходит, следует различать два основных случая использования decltype.
1. decltype(var), когда var — это объявленная переменная (скажем в функции либо как член класса). В этом случае decltype(var) будет иметь в точности тот тип, с которым объявлена переменная.
2. decltype(expr)expr — выражение. В этом случае типом decltype(expr) будет тип, которое могло бы воротить это выражение, с той оговоркой, что decltype(expr) будет иметь тип T& (const T&), если exprвозвращает lvalueT, если expr возвращает rvalue типа Т (const T) и T&& (const T&&), если expr возвращаетxvalue (rvalue reference).

Что значит “могло бы вернуть”? Это значит то, что decltype не вычисляет переданное ему в качестве довода выражение.
Несколько объясняющих примеров:

int i;
decltype(i); // int
decltype(i   1); // int
decltype((i)); // int&
decltype(i = 4); //int&
const int foo();
decltype(foo()) ;// int
int&& foo1();
decltype(foo1()) ;// int&&

В том случае, если мы не знаем lvalue нам вернет выражение, rvalue либо xvalue, а тип применять хочется, дозволено воспользоваться стандартным образцом std::remove_reference, Дабы “очистить” тип от ссылок.

Decltype(auto)

Это новая “фишка” языка, которая войдет в С 14. Она необходима для сохранения семантики decltype при объявлении auto переменных и будет применяться в тех случаях, когда нас не будет устраивать то, что autoотбрасывает ссылки и cv квалификаторы и, допустимо, в связке с новой вероятностью С 14 — итогом типа возвращаемого функцией значения.

const int&& foo();
auto i = foo(); //  i будет иметь тип int
dectype(auto) i2 = foo(); //  i2 будет иметь тип const int&&

В последнем случае мы могли бы написать decltype(foo()), но представьте, если бы взамен foo() было выражение на 2 строчки, а такие в С не редкость.

Результаты

Ну и теперь, загрузив теорию в кэш, дозволено попытаться ответить на вопросы теста.

1.

int foo();
int& foo1();
const int foo2();
const int& foo3();

int main()
{
  auto ri = foo(); // int
  auto ri1 = foo1(); // int
  auto ri2 = foo2(); // int
  auto ri3 = foo3(); // int

  auto& ri4 = foo(); // Не скомпилируется
  auto& ri5 = foo1(); // int&
  auto& ri6 = foo2(); // Не скомпилируется
  auto& ri7 = foo3(); // const int&

  auto&& ri8 = foo(); // int&&
  auto&& ri9 = foo1(); // int&
  auto&& ri10 = foo2(); // const int&&
  auto&& ri11 = foo3(); // const int&

  int k = 5;
  decltype(k)&& rk = k; // Не скомпилируется
  decltype(foo())&& ri12 = foo(); // int&&
  decltype(foo1())&& ri13 = foo1(); // int&

  int i = 3;
  decltype(i) ri14; // int
  decltype((i)) ri15; // int&
}

Скомпилируются ли следующие фрагменты?

2. auto lmbd = [](auto i){...}; // Теперь - нет, но в С  14 - да
3. void foo(auto i);  // Нет
4. decltype(auto) var = some_expression; // Да, в С  14
5. auto var = {1, 2, 3}; // Да, тип = std::initializer_list<int>
6. template<typename T> void foo(T t){}
   foo({1, 2, 3}); // Нет

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB