Почему возвращаемое значение должно быть const для не встроенных типов, а не const для встроенных типов?
Решения 4 и 5 на GotW #6 Const-Correctness упоминают это:
Point GetPoint( const int i ) { return points_[i]; }
Возврат по значению обычно должен быть константным для не встроенных типов возврата.
int GetNumPoints() { return points_.size(); }
.. поскольку int уже является r-значением, и введение в него "const" может помешать созданию экземпляра шаблона и вводит в заблуждение, вводит в заблуждение и, вероятно, приводит к ожирению.
У меня есть следующие вопросы
- Какому экземпляру шаблона мы здесь мешаем?!
- Что именно вводит в заблуждение здесь и почему?
- Почему это различие между не встроенными и встроенными. Я думал, что это плохая практика!
2 ответа
Return-by-value should normally be const for non-builtin return types ..
Это не правильно - одна из относительно немногих ошибок в GotW. const
Значения были сомнительными в C++03 и определенно очень плохими в C++11.
Проблема в том, что в C++03 у rvalues может быть любая вызываемая функция-член, даже неконстантная. Это замечательно, потому что вы можете "swaptimize" или выполнять цепочки методов и другие вещи, которые совершенно хороши и имеют абсолютный смысл, но это также плохо, потому что компилятор не может поймать вас, когда вы делаете что-то глупое, например, присваивание ему или что-то еще, Как правило, плохая идея ограничивать всех от хороших дел, потому что вызывающий может сделать что-то глупое. Намерение это хорошо, но это не правильно.
В C++ 11 это исправлено, потому что вы можете запретить функции-члены вызываться по rvalue, и, во-вторых, потому что для правильной работы семантики перемещения значение rvalue должно быть изменяемым. Вы не можете взять ресурсы из rvalue, если это const
,
Как примечание, причина этого в том, что у примитивных типов всегда была встроенная специальная формулировка, которая делала, например, присвоение rvalues незаконным, поэтому не нужно было пытаться применять это самостоятельно, делая это const
,
Что касается шаблонов, я на самом деле не уверен. Уже было известно, что эта практика была плохой, когда я начал программировать на C++, поэтому мне никогда не приходилось сталкиваться с этим.
Лично я не согласен с рекомендацией ставить const
на возвращаемом значении, не зависящем от семантики перемещения: значение уже является r-значением, и в результате нет большой опасности его случайного изменения. Это часть того, почему поставить const
на не встроенных типах: они могут содержать своего рода бэкдор к значению. Например, std::vector<T>
имеет swap()
метод, который можно использовать для "кражи" содержимого неконстантного значения:
std::vector<int> f();
std::vector<int> value;
f().swap(value);
Точно так же у потоков есть некоторые операторы-члены, которые позволяют вам использовать их с некоторыми встроенными функциями, которые эффективно извлекают ссылку из потока, например:
std::string word;
std::istringstream("hello, world") >> std::skipws >> word;
Без std::skipws
поток является значением, которое не может быть связано с первым аргументом std::operator>> (std::istream&, std::string&)
но использование оператора-члена для манипуляторов возвращает неконстантную ссылку на поток.
const
на встроенные типы фактически не влияет вообще. В частности, при передаче результата функции в шаблон функции (в C++2003) он не может различить const
или неconst
возвращение передается. В результате может показаться, что const
оказывает влияние на встроенный возврат, хотя на самом деле это не так.
Как я уже сказал, я не согласен с правилом, и в C++2011 оно определенно не выполняется, потому что вы хотите иметь возможность в любом случае отключить не встроенное, что было бы предотвращено const
вернуть.