Scott Meyers - Effective Modern C++

Здесь есть возможность читать онлайн «Scott Meyers - Effective Modern C++» весь текст электронной книги совершенно бесплатно (целиком полную версию без сокращений). В некоторых случаях можно слушать аудио, скачать через торрент в формате fb2 и присутствует краткое содержание. Город: Sebastopol, Год выпуска: 2014, ISBN: 2014, Издательство: O’Reilly, Жанр: Программирование, на английском языке. Описание произведения, (предисловие) а так же отзывы посетителей доступны на портале библиотеки ЛибКат.

Effective Modern C++: краткое содержание, описание и аннотация

Предлагаем к чтению аннотацию, описание, краткое содержание или предисловие (зависит от того, что написал сам автор книги «Effective Modern C++»). Если вы не нашли необходимую информацию о книге — напишите в комментариях, мы постараемся отыскать её.

Coming to grips with C++11 and C++14 is more than a matter of familiarizing yourself with the features they introduce (e.g., auto type declarations, move semantics, lambda expressions, and concurrency support). The challenge is learning to use those features
— so that your software is correct, efficient, maintainable, and portable. That's where this practical book comes in. It describes how to write truly great software using C++11 and C++14 — i.e., using
C++.
Topics include:
■ The pros and cons of braced initialization,
specifications, perfect forwarding, and smart pointer make functions
■ The relationships among
,
, rvalue references, and universal references
■ Techniques for writing clear, correct,
lambda expressions
■ How
differs from
, how each should be used, and how they relate to C++'s concurrency API
■ How best practices in “old” C++ programming (i.e., C++98) require revision for software development in modern C++
Effective Modern C++ For more than 20 years,
'
books (
,
, and
) have set the bar for C++ programming guidance. His clear, engaging explanations of complex technical material have earned him a worldwide following, keeping him in demand as a trainer, consultant, and conference presenter. He has a Ph.D. in Computer Science from Brown University.
“After I learned the C++ basics, I then learned how to use C++ in production code from Meyers' series of Effective C++ books. Effective Modern C++ is the most important how-to book for advice on key guidelines, styles, and idioms to use modern C++ effectively and well. Don't own it yet? Buy this one. Now.”
Herb Sutter
Chair of ISO C++ Standards Committee and C++ Software Architect at Microsoft

Effective Modern C++ — читать онлайн бесплатно полную книгу (весь текст) целиком

Ниже представлен текст книги, разбитый по страницам. Система сохранения места последней прочитанной страницы, позволяет с удобством читать онлайн бесплатно книгу «Effective Modern C++», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.

Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

but with an explicit capture, it's easier to see that the viability of the lambda is dependent on divisor's lifetime. Also, writing out the name, “divisor,” reminds us to ensure that divisorlives at least as long as the lambda's closures. That's a more specific memory jog than the general “make sure nothing dangles” admonition that “ [&]” conveys.

If you know that a closure will be used immediately (e.g., by being passed to an STL algorithm) and won't be copied, there is no risk that references it holds will outlive the local variables and parameters in the environment where its lambda is created. In that case, you might argue, there's no risk of dangling references, hence no reason to avoid a default by-reference capture mode. For example, our filtering lambda might be used only as an argument to C++11's std::all_of, which returns whether all elements in a range satisfy a condition:

template

void workWithContainer(const C& container) {

auto calc1 = computeSomeValue1(); // as above

auto calc2 = computeSomeValue2(); // as above

auto divisor= computeDivisor(calc1, calc2); // as above

using ContElemT = typename C::value_type; // type of

// elements in

// container

using std::begin; // for

using std::end; // genericity;

// see Item 13

if (std::all_of( // if all values

begin(container), end(container), // in container

[&](const ContElemT& value) // are multiples

{ return value % divisor == 0; }) // of divisor...

) {

… // they are...

} else {

… // at least one

} // isn't...

}

It's true, this is safe, but its safety is somewhat precarious. If the lambda were found to be useful in other contexts (e.g., as a function to be added to the filterscontainer) and was copy-and-pasted into a context where its closure could outlive divisor, you'd be back in dangle-city, and there'd be nothing in the capture clause to specifically remind you to perform lifetime analysis on divisor.

Long-term, it's simply better software engineering to explicitly list the local variables and parameters that a lambda depends on.

By the way, the ability to use autoin C++14 lambda parameter specifications means that the code above can be simplified in C++14. The ContElemTtypedef can be eliminated, and the if condition can be revised as follows:

if (std::all_of(begin(container), end(container),

[&](const auto& value) // C++14

{ return value % divisor == 0; }))

One way to solve our problem with divisorwould be a default by-value capture mode. That is, we could add the lambda to filtersas follows:

filters.emplace_back( // now

[=](int value) { return value % divisor == 0; } // divisor

); // can't

// dangle

This suffices for this example, but, in general, default by-value capture isn't the anti-dangling elixir you might imagine. The problem is that if you capture a pointer by value, you copy the pointer into the closures arising from the lambda, but you don't prevent code outside the lambda from deleteing the pointer and causing your copies to dangle.

“That could never happen!” you protest. “Having read Chapter 4, I worship at the house of smart pointers. Only loser C++98 programmers use raw pointers and delete.” That may be true, but it's irrelevant because you do, in fact, use raw pointers, and they can, in fact, be deleted out from under you. It's just that in your modern C++ programming style, there's often little sign of it in the source code.

Suppose one of the things Widgets can do is add entries to the container of filters:

class Widget {

public:

… // ctors, etc.

void addFilter() const; // add an entry to filters

private:

int divisor; // used in Widget's filter

};

Widget::addFiltercould be defined like this:

void Widget::addFilter() const

{

filters.emplace_back(

[=](int value) { return value % divisor == 0; }

);

}

To the blissfully uninitiated, this looks like safe code. The lambda is dependent on divisor, but the default by-value capture mode ensures that divisoris copied into any closures arising from the lambda, right?

Wrong. Completely wrong. Horribly wrong. Fatally wrong.

Captures apply only to non- staticlocal variables (including parameters) visible in the scope where the lambda is created. In the body of Widget::addFilter, divisoris not a local variable, it's a data member of the Widgetclass. It can't be captured. Yet if the default capture mode is eliminated, the code won't compile:

void Widget::addFilter() const {

filters.emplace_back( // error!

[](int value) { return value % divisor == 0; } // divisor

); // not

} // available

Furthermore, if an attempt is made to explicitly capture divisor(either by value or by reference — it doesn't matter), the capture won't compile, because divisorisn't a local variable or a parameter:

void Widget::addFilter() const {

filters.emplace_back(

[divisor](int value) // error! no local

{ return value % divisor == 0; } // divisor to capture

);

}

So if the default by-value capture clause isn't capturing divisor, yet without the default by-value capture clause, the code won't compile, what's going on?

The explanation hinges on the implicit use of a raw pointer: this. Every non- staticmember function has a thispointer, and you use that pointer every time you mention a data member of the class. Inside any Widgetmember function, for example, compilers internally replace uses of divisorwith this->divisor. In the version of Widget:: addFilterwith a default by-value capture,

void Widget::addFilter() const {

filters.emplace_back(

[=](int value) { return value % divisor == 0; }

);

}

what's being captured is the Widget's thispointer, not divisor. Compilers treat the code as if it had been written as follows:

void Widget::addFilter() const {

auto currentObjectPtr = this;

filters.emplace_back(

[currentObjectPtr](int value)

{ return value % currentObjectPtr->divisor == 0; }

);

}

Understanding this is tantamount to understanding that the viability of the closures arising from this lambda is tied to the lifetime of the Widgetwhose thispointer they contain a copy of. In particular, consider this code, which, in accord with Chapter 4, uses pointers of only the smart variety:

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Похожие книги на «Effective Modern C++»

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


Отзывы о книге «Effective Modern C++»

Обсуждение, отзывы о книге «Effective Modern C++» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.

x