int typedef a; Это то же самое, что typedef int b; Последний вариант это классический синтаксис. Первый вариант это некие допустимые вариации, которых в C++ довольно много и их лучше не использовать, чтобы не запутать себя и потенциальных читателей кода.
Присваивание применяется к ранее созданным объектам и поэтому включает обязательный этап — освобождение ресурсов этого объекта. Конечно, для простых переменных типа int ничего освобождать не надо, но для более сложных, то том числе и для std::string, это делать обязательно. А вот при инициализации этого делать не нужно, так как мы инициализируем пустой объект, то есть инициализация более простая процедура. При инициализации работает конструктор объекта а при присваивании оператор присваивания, это разные сущности (хотя и связанные).
Здравствуйте, Вызов ::operator new(size) — это вызов стандартной функции выделения памяти, которая в качестве параметра принимает требуемый размер. Эта фунция только выделяет память, ничего больше. Вызов ::new(size) не будет компилироваться, так как в этом случае синтаксически правильной формой должна быть ::new X(/* аргументы конструктора */), где X — это имя класса. В данном случае ::new — это оператор new, который не только выделяет память, но и вызывает конструктор, то есть инициализирует объект. С уважением. Дмитрий Пономарев.
Спасибо за комментарий, очень образное описание сути интерфейса. Многие языки программирования поддерживают интерфейсы на уровне языка (например C#, Java), но вот C++ нет. Приходится эмулировать интерфейсы с помощью чисто виртуальных функций, но при этом возникают достаточно много тонкостей и потенциальных проблем. В своей статье я как раз и попытался разобраться с этими проблемами.
Вообще надо различать вывод аргумента шаблона и вывод типа параметра функции. Они могут не совпадать, типа параметра функции может быть украшен спецификатором ссылки, const и другими модификаторами. В шаблонах функций аргумента шаблона нам доступен, но в лямбдах с auto он будет скрытым.
Одна из идей, которой руководствуются при проектировании ссылок – это сделать ссылку максимально неотличимой от объекта, на который она ссылается. Этим можно объяснить, почему при инициализации C++ ссылки не используется специальный оператор (типа & в случае указателей) и не используется оператор разыменования (типа * в случае указателей) для доступа к объекту. В результате возникают некоторые коллизии, но небольшие, например, нельзя перегрузить функции, у которых параметр передается по значению и по ссылке на константу. Теперь про неизменяемость C++ ссылок, то есть отсутствие нулевых ссылок (обязательная инициализация) и невозможность перенаправить на другую переменную. Посмотрим, какие изменения в языке нужны, если бы ссылки были изменяемые, то есть допускали нулевое значение и перенаправление на другую переменную. Первая проблема – это как быть с присваиванием. Нужно отличать присваивание самих ссылок и присваивание объектов, на которые она ссылается. Для изменяемых ссылок логично реализовывать присваивание как присваивание самих ссылок (а как иначе организовать перенаправление?), но присваивание объектов тоже очень важно в C++. Вторая проблема – это использование самих ссылок в качестве выходного параметра (это еще один вариант реализовать перенаправление). Для этого надо было бы разрешить указатели на ссылку и ссылку на ссылку. Решение всех этих проблем усложнило бы и без того непростой C++, то есть неизменяемость C++ ссылок позволило не переусложнить язык. Теперь посмотрим, что происходит в языках со сборкой мусора, например C#. Объекты, управляемые сборщиком мусора, доступны только через ссылку, таким образом, никаких коллизий между объектом и ссылкой на него быть не может, не нужен оператор разыменования. Ссылки на объекты, управляемые сборщиком мусора, являются изменяемыми, то есть могут быть нулевыми и при присваивании происходит присваивание самих ссылок. Но при этом сами объекты не поддерживают присваивание, для них нельзя перегрузить оператор =, поэтому коллизий не возникает. Если для объекта требуется операция, аналогичная копированию или присваиванию, то в C# надо реализовать специальные методы, типа IClonable.Clone(). Но потребность в таких операциях возникает крайне редко. Ссылки на объекты, управляемые сборщиком мусора, сами могут быть выходными параметрами функции, для таких параметров при объявлении и вызове надо использовать ключевое слово ref.
И я тоже. И [VJG] тоже (с развернутой аргументацией). Здесь мы имеем дело с старыми сишными правилами, которые действительно многих раздражают. Я сам долго думал, как с этим быть, и решил все-таки остановится на каноническом синтаксисе, хотя в своих проектах я его не использую.
В [VJG] forwarding reference переводится как передаваемая ссылка, и мне кажется по смыслу это больше подходит, английские отглагольные определения не всегда можно так просто перевести. А к универсальной ссылке все (включая меня) привыкли благодаря Скотту Мейерсу.
Проблема в том что термин "ссылка" применяется очень широко и, соответственно, имеет разный смысл в разных контекстах. Указатель - это тоже ссылка, если трактовать его в широком смысле. В C++ ссылка (reference) имеет вполне конкретное и достаточно узкое значение.
Да, const T&& не может быть универсальной ссылкой. Но я решил не обсуждать rvalue ссылки на константу, статья и так чрезмерно большая. Согласен, что можно придумать для них применение, но чем-то надо жертвовать.
Согласен, что в C#, Jave термин "ссылочный тип" имеет совсем другой смысл. Но в C++ шаблонах нужно как то называть тип самой ссылки, а не тип переменой, на которую она ссылается, без этого описание шаблонов невозможно. Посмотрите Вандевурд, Дэвид, Джосаттис, Николаи М., Грегор, Дуглас. Шаблоны C++. Справочник разработчика, 2-е изд. В этом справочнике как раз для этого и используется термин "ссылочный тип", так что я ничего не придумывал. Вот цитата
std::is_reference<T>::value
Дает true, если T представляет собой ссылочный тип.
Не соглашусь с Вами. Ссылочный тип - это отдельный тип, но вторичный или производный, то есть образованный от другого типа (как и тип массива). Это хорошо видно, если использовать ссылочный тип в качестве аргумента шаблона, например std::pair<int&, int&>. Ссылочный тип в некотором смысле ущербный, об этом я много писал. А тип переменной, на которую ссылка ссылается - это совсем другое. Здесь мы имеем проблему скорее относящуюся к русскому языку в котором подобные вещи не очень удобно различать. Это примерно тоже, что и разница между константным указателем и указателем на константу.
Попробовал многое из описанного, VS2019 версия 16.10.0. Действительно Microsoft молодец, почти все реализовано. std::format почти 1 к 1 как в C#. По поводу некоторых других фич возникает вопрос: почему такие простые и нужные вещи появились только сейчас (std::numbers::pi, std::string::starts_with()).
Впечатляет. Теперь все эти фичи надо попробовать на уровне семплов, а когда это войдет в повседневную практику, не вполне ясно. Лично я сейчас неплохо ориентируюсь в C++17, но применяю его фичи не часто. Похоже многие руководители не горят желанием переходить на современные версии компиляторов, объясняя это необходимостью совместимости со старыми стандартами. Например в VS2019 по умолчанию C++14, а C++17 и последний стандарт надо включать специально. Правда на днях было обновление до 16.10.0 в котором C++20 поддерживается ощутимо лучше.
Определять полный набор операторов сравнения для пользовательских типов приходится очень редко. Обычно определяют < для сортировки и set или == и хеш для хеш таблиц. Особого восторга эта фича не вызвала.
int typedef a
;Это то же самое, что
typedef int b;
Последний вариант это классический синтаксис. Первый вариант это некие допустимые вариации, которых в C++ довольно много и их лучше не использовать, чтобы не запутать себя и потенциальных читателей кода.
Да.
И еще
std::string s{ "meow" };
std::string s = { "meow" };
auto s = std::string("meow");
auto s = std::string{ "meow" };
Все эти варианты вызывают один и тот же конструктор.
Присваивание применяется к ранее созданным объектам и поэтому включает обязательный этап — освобождение ресурсов этого объекта. Конечно, для простых переменных типа int ничего освобождать не надо, но для более сложных, то том числе и для std::string, это делать обязательно. А вот при инициализации этого делать не нужно, так как мы инициализируем пустой объект, то есть инициализация более простая процедура. При инициализации работает конструктор объекта а при присваивании оператор присваивания, это разные сущности (хотя и связанные).
Полностью согласен.
Здравствуйте,
Вызов ::operator new(size) — это вызов стандартной функции выделения памяти, которая в качестве параметра принимает требуемый размер. Эта фунция только выделяет память, ничего больше.
Вызов ::new(size) не будет компилироваться, так как в этом случае синтаксически правильной формой должна быть ::new X(/* аргументы конструктора */), где X — это имя класса. В данном случае ::new — это оператор new, который не только выделяет память, но и вызывает конструктор, то есть инициализирует объект.
С уважением. Дмитрий Пономарев.
Спасибо, рад, что мои материалы оказались полезными.
Спасибо за комментарий, очень образное описание сути интерфейса. Многие языки программирования поддерживают интерфейсы на уровне языка (например C#, Java), но вот C++ нет. Приходится эмулировать интерфейсы с помощью чисто виртуальных функций, но при этом возникают достаточно много тонкостей и потенциальных проблем. В своей статье я как раз и попытался разобраться с этими проблемами.
Вообще надо различать вывод аргумента шаблона и вывод типа параметра функции. Они могут не совпадать, типа параметра функции может быть украшен спецификатором ссылки,
const
и другими модификаторами. В шаблонах функций аргумента шаблона нам доступен, но в лямбдах сauto
он будет скрытым.Спасибо за конструктивную критику.
А вот использовать ссылку в качестве параметра функции безопасно
Здесь я имел в виду передача ссылки в глубину стека, конечно возврат этой же ссылки может все сломать и я про это писал.
И про "ссылку" получаемую из итератора
std::vector<bool> ...
Согласен, что итераторы, возвращающие прокси-объекты, не обсуждал. Эта тема подробно обсуждается у Скотта Мейерса.
Не сказано так же о том, что взятие ссылки на временный объект продлевает его срок жизни до конца жизни самой ссылки.
Не согласен, этому посвящен раздел 4.1.
Одна из идей, которой руководствуются при проектировании ссылок – это сделать ссылку максимально неотличимой от объекта, на который она ссылается. Этим можно объяснить, почему при инициализации C++ ссылки не используется специальный оператор (типа
&
в случае указателей) и не используется оператор разыменования (типа*
в случае указателей) для доступа к объекту. В результате возникают некоторые коллизии, но небольшие, например, нельзя перегрузить функции, у которых параметр передается по значению и по ссылке на константу.Теперь про неизменяемость C++ ссылок, то есть отсутствие нулевых ссылок (обязательная инициализация) и невозможность перенаправить на другую переменную. Посмотрим, какие изменения в языке нужны, если бы ссылки были изменяемые, то есть допускали нулевое значение и перенаправление на другую переменную. Первая проблема – это как быть с присваиванием. Нужно отличать присваивание самих ссылок и присваивание объектов, на которые она ссылается. Для изменяемых ссылок логично реализовывать присваивание как присваивание самих ссылок (а как иначе организовать перенаправление?), но присваивание объектов тоже очень важно в C++. Вторая проблема – это использование самих ссылок в качестве выходного параметра (это еще один вариант реализовать перенаправление). Для этого надо было бы разрешить указатели на ссылку и ссылку на ссылку. Решение всех этих проблем усложнило бы и без того непростой C++, то есть неизменяемость C++ ссылок позволило не переусложнить язык.
Теперь посмотрим, что происходит в языках со сборкой мусора, например C#. Объекты, управляемые сборщиком мусора, доступны только через ссылку, таким образом, никаких коллизий между объектом и ссылкой на него быть не может, не нужен оператор разыменования. Ссылки на объекты, управляемые сборщиком мусора, являются изменяемыми, то есть могут быть нулевыми и при присваивании происходит присваивание самих ссылок. Но при этом сами объекты не поддерживают присваивание, для них нельзя перегрузить оператор
=
, поэтому коллизий не возникает. Если для объекта требуется операция, аналогичная копированию или присваиванию, то в C# надо реализовать специальные методы, типаIClonable.Clone()
. Но потребность в таких операциях возникает крайне редко. Ссылки на объекты, управляемые сборщиком мусора, сами могут быть выходными параметрами функции, для таких параметров при объявлении и вызове надо использовать ключевое словоref
.И я тоже. И [VJG] тоже (с развернутой аргументацией). Здесь мы имеем дело с старыми сишными правилами, которые действительно многих раздражают. Я сам долго думал, как с этим быть, и решил все-таки остановится на каноническом синтаксисе, хотя в своих проектах я его не использую.
В [VJG] forwarding reference переводится как передаваемая ссылка, и мне кажется по смыслу это больше подходит, английские отглагольные определения не всегда можно так просто перевести. А к универсальной ссылке все (включая меня) привыкли благодаря Скотту Мейерсу.
Проблема в том что термин "ссылка" применяется очень широко и, соответственно, имеет разный смысл в разных контекстах. Указатель - это тоже ссылка, если трактовать его в широком смысле. В C++ ссылка (reference) имеет вполне конкретное и достаточно узкое значение.
Да,
const T&&
не может быть универсальной ссылкой. Но я решил не обсуждать rvalue ссылки на константу, статья и так чрезмерно большая. Согласен, что можно придумать для них применение, но чем-то надо жертвовать.Спасибо! Вопросы интересные, но не простые, с ходу ответить не могу. Подробнее напишу немного позднее.
Согласен, что в C#, Jave термин "ссылочный тип" имеет совсем другой смысл. Но в C++ шаблонах нужно как то называть тип самой ссылки, а не тип переменой, на которую она ссылается, без этого описание шаблонов невозможно. Посмотрите Вандевурд, Дэвид, Джосаттис, Николаи М., Грегор, Дуглас. Шаблоны C++. Справочник разработчика, 2-е изд. В этом справочнике как раз для этого и используется термин "ссылочный тип", так что я ничего не придумывал. Вот цитата
std::is_reference<T>::value
Дает
true
, еслиT
представляет собой ссылочный тип.Не соглашусь с Вами. Ссылочный тип - это отдельный тип, но вторичный или производный, то есть образованный от другого типа (как и тип массива). Это хорошо видно, если использовать ссылочный тип в качестве аргумента шаблона, например
std::pair<int&, int&>
. Ссылочный тип в некотором смысле ущербный, об этом я много писал. А тип переменной, на которую ссылка ссылается - это совсем другое. Здесь мы имеем проблему скорее относящуюся к русскому языку в котором подобные вещи не очень удобно различать. Это примерно тоже, что и разница между константным указателем и указателем на константу.