» » » Современные возможности C++, о которых надо знать всем программистам

 

Современные возможности C++, о которых надо знать всем программистам

Автор: admin от 17-05-2019, 12:05, посмотрело: 198

Автор материала, перевод которого мы сегодня публикуем, говорит, что C++, в его современном виде, если сравнивать его с тем, чем был этот язык несколько лет назад, значительно изменился в лучшую сторону. Конечно, эти изменения произошли далеко не сразу. Например, в былые времена C++ не хватало динамичности. Непросто было найти человека, который мог бы сказать, что он питает к этому языку нежные чувства. Всё изменилось тогда, когда те, кто отвечает за стандартизацию языка, решили дать ход новшествам. В 2011 году C++ стал динамическим языком, языком, который постоянно развивается и вызывает у программистов куда больше положительных эмоций.



Не стоит думать, что язык стал проще. Его всё ещё можно назвать одним из самых сложных широко используемых языков программирования, если не самым сложным. Но современный C++ стал гораздо дружелюбнее, чем раньше.



Современные возможности C++, о которых надо знать всем программистам


Сегодня мы поговорим о некоторых новых возможностях языка (начиная с C++ 11, которому, кстати, уже 8 лет), знать о которых будет полезно любому программисту.

этого репозитория, посвящённого современным возможностям C++:




  • [] — выражение ничего не захватывает. Это значит, что в лямбда-выражении нельзя использовать локальные переменные из внешней по отношению к нему области видимости. В выражении можно использовать лишь параметры.

  • [=] — выражение захватывает значения локальных объектов (то есть — локальные переменные, параметры). Это значит, что их можно использовать, но не модифицировать.

  • [&] — выражение захватывает ссылки на локальные объекты. Их можно модифицировать, это показано в следующем примере.

  • [this] — выражение захватывает значение указателя this.

  • [a, &b] — выражение захватывает значение объекта a и ссылку на объект b.



В результате, если внутри лямбда-функции нужно преобразовать данные в какой-то другой формат, можно воспользоваться вышеописанными механизмами. Рассмотрим пример:



std::vector<int> data = {2, 4, 4, 1, 1, 3, 9};
int factor = 7;
for_each(begin(data), end(data), [&factor](int &val) { // получаем доступ к factor по ссылке
    val = val * factor;
    factor--; // #1 это возможно из-за того, что лямбда-выражение получает доступ к factor по ссылке
});

for(int val: data) {
    std::cout  val  ' '; // 14 24 20 4 3 6 9
}


Здесь, если бы доступ к переменной factor осуществлялся бы по значению (тогда при описании лямбда-выражения использовалась бы маска переменных [factor]), то в строке #1 значение factor менять было бы нельзя — просто потому что у нас не было бы прав на выполнение такой операции. В данном же примере право на подобные действия у нас есть. В таких ситуациях важно не злоупотреблять возможностями, которые даёт доступ к переменным по ссылке.



Кроме того, обратите внимание на то, что доступ к val тоже осуществляется по ссылке. Это позволяет обеспечить то, что изменения данных, происходящие в лямбда-функции, влияют на vector.



Выражения инициализации переменных внутри конструкций if и switch



Это новшество C++ 17 очень понравилось мне сразу после того, как я о нём узнал. Рассмотрим пример:



std::set<int> input = {1, 5, 3, 6};

if(auto it = input.find(7); it==input.end()){ // первая часть - инициализация, вторая - условие
    std::cout  7  " not found"  std:endl;
}
else {
    // у этого блока else есть доступ к области видимости it
    std::cout  7  " is there!"  std::endl;
}


Получается, что теперь можно выполнять инициализацию переменных и сравнения с их использованием в одном блоке if или switch. Это способствует написанию аккуратного кода. Вот как выглядит схематичное описание рассмотренной конструкции:



if( init-statement(x); condition(x)) {
    // выполнить некие действия
} else {
    // здесь можно работать с x
    // выполнить некие действия
}


Выполнение вычислений во время компиляции с использованием constexpr



Ключевое слово constexpr даёт нам замечательные возможности. Предположим, у нас есть некое выражение, которое надо вычислить, при этом его значение, после инициализации им соответствующей переменной, меняться не будет. Такое выражение можно вычислить заранее и использовать его как макрос. Или, что стало возможным в C++ 11, воспользоваться ключевым словом constexpr.



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



constexpr double fib(int n) { // функция объявлена с использованием ключевого слова constexpr
    if(n == 1) return 1;
    return fib(n-1) * n;
}

int main() {
    const long long bigval = fib(20);
    std::coutbigvalstd::endl;
}


Это — весьма распространённый пример использования constexpr.



Так как мы объявили функцию для вычисления чисел Фибоначчи как constexpr, компилятор может заранее вычислить значение fib(20) во время компиляции программы. В результате, после компиляции строку const long long bigval = fib(20); можно будет заменить на const long long bigval = 2432902008176640000;.



Обратите внимание на то, что аргумент, переданный функции, представлен константой. Это — важная особенность использования функций, объявленных с использованием ключевого слова constexpr. Передаваемые им аргументы тоже должны быть объявлены с помощью ключевого слова constexpr или с помощью ключевого слова const. В противном случае подобные функции будут вести себя как обычные функции, то есть — во время компиляции не будут проводиться заблаговременное вычисление их значений.



Переменные тоже можно объявлять с помощью ключевого слова constexpr. В подобном случае, как несложно догадаться, значения этих переменных должны быть вычислены во время компиляции. Если сделать этого нельзя — будет выведено сообщение об ошибке компиляции.



Интересно отметить, что позже, в C++ 17, появились конструкции constexpr-if и constexpr-lambda.



Структуры данных tuple



Так же как и структура данных pair, структура данных tuple (кортеж) представляет собой коллекцию значений разных типов фиксированного размера. Вот пример:



auto user_info = std::make_tuple("M", "Chowdhury", 25); // используем auto для автоматического вывода типа

// доступ к данным
std::get<0>(user_info);
std::get<1>(user_info);
std::get<2>(user_info);

// в C++ 11 для выполнения привязок использовали tie

std::string first_name, last_name, age;
std::tie(first_name, last_name, age) = user_info;

// в C++ 17, конечно, то же самое делается гораздо удобнее
auto [first_name, last_name, age] = user_info;


Иногда вместо структуры данных tuple удобнее использовать std::array. Эта структура данных похожа на простые массивы, используемые в языке C, снабжённые дополнительными возможностями из стандартной библиотеки C++. Эта структура данных появилась в C++ 11.



Автоматический вывод типа аргумента шаблона класса



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



std::pair<std::string, int> user = {"M", 25};


С выходом C++ 17 эту конструкцию стало можно заменить на эту:



std::pair user = {"M", 25};


Вывод типов выполняется неявно. Этим механизмом ещё удобнее пользоваться в том случае, когда речь идёт о кортежах. А именно, раньше приходилось писать следующее:



std::tuple<std::string, std::string, int> user ("M", "Chy", 25);


Теперь же то же самое выглядит так:



std::tuple user2("M", "Chy", 25);


Тут стоит отметить, что эти возможности не покажутся чем-то достойным внимания тем, кто не особенно хорошо знаком с шаблонами C++.



Умные указатели



Работа с указателями в C++ может оказаться настоящим кошмаром. Благодаря той свободе, которую язык даёт программисту, порой ему бывает очень непросто, как говорится, «не выстрелить себе в ногу». Во многих случаях к такому вот «выстрелу» программиста подталкивают именно указатели.



К нашему счастью, в C++ 11 появились умные указатели, которые гораздо удобнее обычных. Они помогают программисту избегать утечек памяти, освобождая, когда это возможно, ресурсы. Кроме того, они дают гарантию безопасности по исключениям.



Итоги



Вот хороший репозиторий, в который, полагаем, интересно будет заглядывать тем, кто следит за новшествами C++. В этом языке постоянно появляется что-то новое. Здесь мы коснулись лишь нескольких современных возможностей языка. На самом деле их очень много. Вполне возможно то, что мы о них ещё поговорим.



Уважаемые читатели! Какие современные возможности C++ кажутся вам самыми интересными и полезными?



Современные возможности C++, о которых надо знать всем программистам

Источник: Хабр / Интересные публикации

Категория: Веб-разработка

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *