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&&
.
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/
https://devblogs.microsoft.com/oldnewthing/20230803-00/?p=108532
https://devblogs.microsoft.com/oldnewthing/20240510-00/?p=109742
Сравнение реализации строк из мейнстримных компиляторов (ms, gcc и clang)
пример кода, где появляется " например, 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'ами. Куда пишем - "слева", что пишем - "справа".
lvalues, rvalues, glvalues, prvalues, xvalues, помогите! -