Какие идиомы C++ устарели в C++ 11?
В новом стандарте есть новые способы делать вещи, и многие из них лучше, чем старые, но старый способ по-прежнему прекрасен. Также ясно, что новый стандарт официально не унижает, по причинам обратной совместимости. Таким образом, остается вопрос:
Какие старые способы кодирования определенно уступают стилям С++ 11, и что теперь мы можем сделать вместо этого?
Отвечая на это, вы можете пропустить очевидные вещи, такие как "использовать автоматические переменные".
Ответы
Ответ 1
- Финальный класс: C++ 11 предоставляет
final
спецификатор, чтобы предотвратить вывод класса - C++ 11 лямбда-выражений существенно сокращают потребность в именованных классах объектов функций (функторов).
- Конструктор перемещения: Волшебные способы работы
std::auto_ptr
больше не нужны из-за первоклассной поддержки ссылок на rvalue. - Безопасный бул: это было упомянуто ранее. Явные операторы C++ 11 устраняют эту очень распространенную идиому C++ 03.
- Сжатие для подгонки: многие C++ 11 STL-контейнеров предоставляют
shrink_to_fit()
член shrink_to_fit()
, которая должна исключить необходимость замены на временную. - Временный базовый класс: некоторые старые библиотеки C++ используют эту довольно сложную идиому. С семантикой ходов она больше не нужна.
- Перечисления типа Safe Enum очень безопасны в C++ 11.
- Запрещение выделения кучи. Синтаксис
= delete
является гораздо более прямым способом сказать, что определенная функциональность явно запрещена. Это применимо для предотвращения выделения кучи (т.е. =delete
для operator new
члена- operator new
), предотвращения копирования, назначения и т.д. - Templated typedef: шаблоны псевдонимов в C++ 11 уменьшают потребность в простых шаблонных определениях типов. Тем не менее, генераторы сложных типов все еще нуждаются в мета-функциях.
- Некоторые численные вычисления во время компиляции, такие как Фибоначчи, могут быть легко заменены с использованием обобщенных константных выражений
-
result_of
: использование шаблона класса result_of
должно быть заменено на decltype
. Я думаю, что result_of
использует decltype
когда он доступен. - Инициализаторы членов класса сохраняют типизацию для инициализации по умолчанию нестатических членов со значениями по умолчанию.
- В новом C++ 11 код
NULL
должен быть переопределен как nullptr
, но посмотрите разговор STL, чтобы узнать, почему они решили против этого. - Фанатики шаблонов выражений рады иметь синтаксис функции завершающего типа возврата в 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
как константы нулевого указателя. Но вы должны сделать код так, как если бы это было.
Ответ 4
Безопасная идиома bool → explicit operator bool()
.
Конкретные конструкторы копирования (boost:: noncopyable) → X(const X&) = delete
Имитация конечного класса с частным деструктором и виртуальным наследованием → class X final
Ответ 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
Возврат по значению больше не проблема. С семантикой перемещения и/или оптимизацией возвращаемого значения (зависит от компилятора) функции кодирования более естественны без затрат и затрат (большую часть времени).