Pull to refresh

Comments 23

Отличная статья! А еще есть perfect forwarding, который тоже может сбить с толку. Потому что разработчики языка решили сэкономить и использовали оператор && не только для семантики перемещения, но и для еще одной цели - когда компилятор сам выбирает способ передачи, по значению или по ссылке, но только в шаблонах:)

На самом деле это довольно легко. Если параметр функции - это T&& , и T - это параметр шаблона функции, то применяется folding, который и есть ключ для perfect forwarding - вычёркивание двойных &&, если их больше 2: аргумент int a имеет тип int& , и T&& становится int&&& , что есть int& && - int& после свёртки. А std::move(a) имеет тип int&& и T&& становится int&&&&, что есть int&& && - int&& .

Прочитав это, я радуюсь, что никогда не писал продуктовый код на C++ (и надеюсь не буду) :-)

Это все больше пригодится на собеседовании, а не в продуктовом коде

Perfect forwarding никогда не будет передачей по значению, всегда только по ссылке.

И это не совсем специальный синтаксис, а результат взаимодействия reference collapsing с дедукцией типов. Правила дедукции может и специальные, но как мне кажется нельзя сказать что кто-то сэкономил.

Не могу понять, что такое, имеющее имя, но которое нельзя переместить. Можно пример кода, где появляется " например, rvalue‑ссылка из функции"?

по умолчанию переменная не перемещается а копируется.

void foo(string str) {...}
...
string str;
foo(str);

с таки кодом внутри foo будет доступ к независимой копии str

а вот, если сделать так: foo(std::move(str));
копия не будет создана, содержимое строки будет перемещено в функцию, а str снаружи станет пустой

rvalue‑ссылка из функции

не совсем понял что имеется ввиду, возможно что-то такое

struct A{
string str;
string&& extract() { return str; }
};

содержимое строки будет перемещено в функцию, а str снаружи станет пустой

Не совсем верно без деталей функции. Короткие строки будут скопированы. Для длинных строк тоже есть варианты. Если параметр функции - ссылка, то ничего не произойдёт, если rvalue-ссылка, то ничего, перемещение или копирование зависят от тела функции, если не ссылка, то строка переместится в аргумент функции.

Короткие строки будут скопированы. Для длинных строк тоже есть варианты.

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

 Если параметр функции - ссылка

так а я привел прототип там параметр передается по значению

Когда я писал ответ, прототипа функции не было.

нет никаких вариантов, всё строго и однозначно.

Ничего однозначного нет. После перемещения строка находится в неопределёном состоянии.

Все известные реализации стандартной библиотеки имеют оптимизации для коротких строк, потому что имея много байтов на стеке для указателя + размера + ёмкости, выделять ещё 1 нулевой байт на куче для пустой строки - это расточительство. Вот на русском https://pvs-studio.ru/ru/blog/terms/6658/

пример кода, где появляется " например, rvalue‑ссылка из функции"

std::move(x) возвращает rvalue-ссылку на x.

"нельзя переместить" означает что не произойдет связывание с аргументом типа revalue reference. То есть не вызовется конструктор перемещения, например.

Для себя использую такую шпаргалку:

  • lvalue - любое выражение, чей тип является lvalue ссылкой или которое является именованой переменной.

  • xvalue - любое выражение, тип которого является rvalue ссылкой, за исключением именованой переменной.

  • prvalue - любое выражение, тип которого не является ссылкой и которое не является именованой переменной.

Как же я люблю заходить в эти статьи про текущее состояние С++ и охреневать с того, какой еще вырвиглазно сложной неочевидной мути намудрили в этом языке. И радоваться что уже больше десяти лет на нем не пишу, и скорее всего никогда уже не буду. И мне не надо изучать всё это метапрограммирование на шаблонах и вот эти непонятные штуки.

Это ппц какой-то, будто сговорились напихать в него самых каких-то сложных и неочевидных концепций. Что это, зачем это всё, кто все эти люди? Как другие языки без этого всего живут?

а чему собсвенно радоваться? по мне это тоже самое что радоватсья отсутствию необходимости обувь покупать и шнурки завязывать когда ноги отпилили. На C++ можно просто не пользоваться тем чего не понимаешь.

Нет, нельзя: если кто-то другой в команде или нужной тебе библиотеке чем-то таким воспользовался - тебе с этим жить.

Ну а сравнивать необходимость писать на С++ с наличием ног - это прямо вау. Даже не знаю, с чем сравнить. С фанатизмом наверное 🤔

Соглашусь. В свое время наелся этого когда изучал Scala. Которая примерно на половину состояла из "как джава только лучше", а на вторую половину из "ехал функтор через монаду". И к сожалению разделить их было невозможно, так как авторы библиотек прям обожали тащить в свой код хардкорную функциональщину, и ты хочешь-не хочешь, а вынужден был в ней разбираться и ковыряться.

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

Нет, нельзя: если кто-то другой в команде или нужной тебе библиотеке чем-то таким воспользовался - тебе с этим жить.

Ну а сравнивать необходимость писать на С++ с наличием ног - это прямо вау. Даже не знаю, как описать... Как "фанатизм" наверное 🤔

Категории выражений не менялись в C++ с 11 стандарта, так что 10 лет назад Вы уже должны были это знать

Не, даже больше 10 лет. Я на С++ писал когда в геймдеве работал в конце нулевых и начале десятых. Тогда только-только C++0x выходил, впервые за долгое время в языке что-то всерьез менялось. При этом до индустрии эти изменения еще катились бы много лет, так что воспользоваться ими я не успел.

А потом кажется у кого-то отказали тормоза, но я это уже не застал к счастью.

Это просто формализация того, что и так уже было. Только было как раз неочевидно (потому в разных реализациях, бывало, понималось по-разному). А сейчас просто взяли и привели это все в систему. Да, получилась она не самая простая, но это явно лучше, чем оставлять все как было. Так что нет, вы со своим оценочным и явно больше эмоциональным, чем конструктивным, суждением - не правы.

Компилятор clang преобразует C++ код в llvm-IR. Представление llvm-IR - это почти язык Си, но только с invoke/landigpad'ами (и то можно обойтись парой setjmp/longjmp). По сути всё lower-ится в Си. После lowering'а будут только глобалы/локалы и указатели с load/store'ами. Куда пишем - "слева", что пишем - "справа".

Sign up to leave a comment.

Articles