Какие идиомы C++ устарели в C++ 11?

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

Какие старые способы кодирования определенно уступают стилям С++ 11, и что теперь мы можем сделать вместо этого?

Отвечая на это, вы можете пропустить очевидные вещи, такие как "использовать автоматические переменные".

Ответ 1

  1. Финальный класс: C++ 11 предоставляет final спецификатор, чтобы предотвратить вывод класса
  2. C++ 11 лямбда-выражений существенно сокращают потребность в именованных классах объектов функций (функторов).
  3. Конструктор перемещения: Волшебные способы работы std::auto_ptr больше не нужны из-за первоклассной поддержки ссылок на rvalue.
  4. Безопасный бул: это было упомянуто ранее. Явные операторы C++ 11 устраняют эту очень распространенную идиому C++ 03.
  5. Сжатие для подгонки: многие C++ 11 STL-контейнеров предоставляют shrink_to_fit() член shrink_to_fit(), которая должна исключить необходимость замены на временную.
  6. Временный базовый класс: некоторые старые библиотеки C++ используют эту довольно сложную идиому. С семантикой ходов она больше не нужна.
  7. Перечисления типа Safe Enum очень безопасны в C++ 11.
  8. Запрещение выделения кучи. Синтаксис = delete является гораздо более прямым способом сказать, что определенная функциональность явно запрещена. Это применимо для предотвращения выделения кучи (т.е. =delete для operator new члена- operator new), предотвращения копирования, назначения и т.д.
  9. Templated typedef: шаблоны псевдонимов в C++ 11 уменьшают потребность в простых шаблонных определениях типов. Тем не менее, генераторы сложных типов все еще нуждаются в мета-функциях.
  10. Некоторые численные вычисления во время компиляции, такие как Фибоначчи, могут быть легко заменены с использованием обобщенных константных выражений
  11. result_of: использование шаблона класса result_of должно быть заменено на decltype. Я думаю, что result_of использует decltype когда он доступен.
  12. Инициализаторы членов класса сохраняют типизацию для инициализации по умолчанию нестатических членов со значениями по умолчанию.
  13. В новом C++ 11 код NULL должен быть переопределен как nullptr, но посмотрите разговор STL, чтобы узнать, почему они решили против этого.
  14. Фанатики шаблонов выражений рады иметь синтаксис функции завершающего типа возврата в C++ 11. Не более 30 строк возврата!

Я думаю, что я остановлюсь там!

Ответ 2

В какой-то момент времени утверждалось, что нужно возвращать значение const вместо значения:

const A foo();
^^^^^

Это было в основном безвредно в С++ 98/03 и, возможно, даже попало в несколько ошибок, которые выглядели следующим образом:

foo() = a;

Но возврат с помощью const противопоказан в С++ 11, поскольку он запрещает семантику перемещения:

A a = foo();  // foo will copy into a instead of move into it

Так просто расслабьтесь и закодируйте:

A foo();  // return by non-const value

Ответ 3

Как только вы можете отказаться от 0 и NULL в пользу nullptr, сделайте это!

В нестандартном коде использование 0 или NULL не является таким большим делом. Но как только вы начинаете передавать вокруг нулевых констант указателя в общем коде, ситуация быстро меняется. Когда вы передаете 0 в template<class T> func(T) T, выводится как int, а не как константа нулевого указателя. И после этого он не может быть преобразован обратно в константу нулевого указателя. Это каскадирует в трясину проблем, которые просто не существуют, если юниверс использовал только nullptr.

С++ 11 не осуждает 0 и NULL как константы нулевого указателя. Но вы должны сделать код так, как если бы это было.

Ответ 5

Одна из вещей, которые просто заставляют вас избегать написания базовых алгоритмов в С++ 11, - это доступность лямбда в сочетании с алгоритмами, предоставляемыми стандартной библиотекой.

Я использую их сейчас, и невероятно, как часто вы просто рассказываете, что хотите делать, используя count_if(), for_each() или другие алгоритмы вместо того, чтобы снова писать проклятые циклы.

Как только вы используете компилятор С++ 11 с полной стандартной библиотекой С++ 11, у вас нет хорошего оправдания, чтобы не использовать стандартные алгоритмы для создания. Лямбда просто убил его.

Почему?

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

В принципе, алгоритмы чтения, сделанные со стандартными алгоритмами, намного проще, чем слова, скрывающие детали реализации циклов.

Я предполагаю, что теперь нужно думать только об алгоритмах более высокого уровня, о том, что мы используем алгоритмы более низкого уровня.

Ответ 6

Менее часто нужно выполнять пользовательские версии swap. В С++ 03 для предотвращения дорогостоящих и металических копий часто требуется эффективное небрасывание swap, и поскольку std::swap использует две копии, часто нужно настраивать swap. В С++ std::swap использует move, и поэтому фокус сдвигается на реализацию эффективных и не метательных конструкторов перемещения и операторов переадресации. Поскольку для них по умолчанию часто просто отлично, это будет намного меньше, чем в С++ 03.

Как правило, трудно предсказать, какие идиомы будут использоваться, поскольку они создаются через опыт. Мы можем ожидать, что "Эффективный С++ 11" может быть в следующем году и "Coding 11 Coding Standards" только через три года, потому что необходимого опыта еще нет.

Ответ 7

Я не знаю его имени, но код С++ 03 часто использовал следующую конструкцию в качестве замены отсутствующего назначения переноса:

std::map<Big, Bigger> createBigMap(); // returns by value

void example ()
{
  std::map<Big, Bigger> map;

  // ... some code using map

  createBigMap().swap(map);  // cheap swap
}

Это предотвратило любое копирование из-за копирования в сочетании с swap выше.

Ответ 8

Когда я заметил, что компилятор, использующий стандарт С++ 11, больше не повреждает следующий код:

std::vector<std::vector<int>> a;

для якобы содержащего оператора → , я начал танцевать. В более ранних версиях нужно было бы сделать

std::vector<std::vector<int> > a;

Что еще хуже, если вам когда-либо приходилось отлаживать это, вы знаете, насколько ужасны сообщения об ошибках, которые возникают из этого.

Я, однако, не знаю, было ли это "очевидно" для вас.

Ответ 9

Возврат по значению больше не проблема. С семантикой перемещения и/или оптимизацией возвращаемого значения (зависит от компилятора) функции кодирования более естественны без затрат и затрат (большую часть времени).