Здравствуйте, σ, Вы писали:
>> Является ли данный код корректным? σ>Нет. std::cout и std::endl не определены.
А с чего вы взяли что они не определены? Стандартом требуется что бы были заголовочные файлы для std::cout и std::endl?
Да, является корректным. Потому, что x — это lvalue. Вообще, следует запомнить одно простое правило: как только rvalue объект приобретает имя, он превращается в lvalue. А чтобы обращаться с ним как с rvalue, следует применять std::forward или std::move.
--
Не можешь достичь желаемого — пожелай достигнутого.
R>Да, является корректным. Потому, что x — это lvalue. Вообще, следует запомнить одно простое правило: как только rvalue объект приобретает имя, он превращается в lvalue. А чтобы обращаться с ним как с rvalue, следует применять std::forward или std::move.
Здравствуйте, Максим Рогожин, Вы писали:
МР>Здравствуйте, σ, Вы писали:
>>> Является ли данный код корректным? σ>>Нет. std::cout и std::endl не определены. МР>А с чего вы взяли что они не определены? Стандартом требуется что бы были заголовочные файлы для std::cout и std::endl?
обратить внимание на defined in header... иначе да -- нет header'a -- не будет определения %)
странно что такой вопрос вообще возник... как ты себе представляешь, чтобы какие-то сущности появлялись "из ниоткуда"... это же не JS какой-нибудь %)
здесь максимум предопределено несколько макросов (ну чуть больше после С++14/17) и на вскидку вспоминается еще результат `typeid`, но чтобы с ним работать адекватно также нужно подключить `typeinfo` header.
может понимание того, в какой машинный код обычно компилится С\С++ поможет разобраться...
МР>В обоих случаях, x это переменная на стеке, т.е. lvalue. Но,
Переменная "на стэке" (т.е. в памяти) уже само по себе означает что у нее есть адрес -- т.е. с точки зрения C/C++ его можно "взять".
Даже если это параметр функции (которые по соглашению передаются через стэк), внутри вызванной функции у него будет адрес (и да, его можно "взять", какого бы типа он там ни был в С/С++). Если компилятор хотел было оптимизировать эту функцию (допустим она не экспортируемая) и передавать в нее параметр через регистр, но оказалось, что внутри "кто-то берет адрес у параметра", то компилятор сделает так, чтобы это было осуществимо... т.е. просто не станет оптимизировать передачу этого параметра %)
"временные" (или более обще rvalue) объекты в С/С++, на уровне машинных команд это
* например значения возвращаемые через регистры
* результаты "подвыражений" (например `foo(some + other)`) -- тоже как правило компилятор хранит в регистрах до момента использования, который как правило наступает где-то "рядом"
* или например т.н. непосредственные операнды (инструкций) -- литералы к примеру как в `bar += 100500;` вот это вот `100500` будет лежать непосредственно в сегменте кода, а не данных...
т.е. в целом любая сущность не в памяти предназначенной для хранения данных (имеется ввиду сегменты `.data`, `.const`, `.bss` & etc, но не `.text`).
Сущности же, которые тем или иным образом попадают в `.data`, by design имеют адрес, который можно "взять" в С/С++ -- таким образом в этих языках они получаются `lvalue` тем или иным образом.
Возмем `f(h())` в твоем примере. Результат `h()` вероятнее всего просто "перекочует" из регистра (где по соглашению `h()` его должен будет вернуть) сразу в стэк как параметр для `f()`. Если ты "сохранишь" этот "промежуточный" результат в "переменную", компилятор вероятнее всего, даже не будет для нее выделять место в стэке, если единственное использование такой переменной это тутже быть переданной в `f(tmp)`. Но как только ты возмешь адрес у этой переменной, компилятор будет вынужден/обязан "дать" ей место в стэке (для автоматических переменных). Просто потому, что у нее должен быть адрес %) И в этот момент в С\С++ она перестает быть `rvalue` и становится `lvalue` %)
С точки зрения машинных команд: все, что находится в памяти (RAM), безусловно имеет адрес. Просто в С/С++ этот факт (абстракция "память") "перекочевал" с некоторыми ограничениями в виде разделения r/x/l values %) -- мол не у всего можно взять адрес... С одной стороны это "развязывает компилятору руки" для оптимизаций, с другой, в целом то не все эти аппаратные фичи нужны программисту...
Здравствуйте, zaufi, Вы писали:
Z>обратить внимание на defined in header... иначе да -- нет header'a -- не будет определения %)
Z>странно что такой вопрос вообще возник... как ты себе представляешь, чтобы какие-то сущности появлялись "из ниоткуда"... это же не JS какой-нибудь %) Z>здесь максимум предопределено несколько макросов (ну чуть больше после С++14/17) и на вскидку вспоминается еще результат `typeid`, но чтобы с ним работать адекватно также нужно подключить `typeinfo` header.
Ну и что, а может у меня экзотическая реализация компилятора — этот header в самом компиляторе захардкоден — это что не соответствовало бы стандарту?
Здравствуйте, Максим Рогожин, Вы писали:
МР>Здравствуйте, zaufi, Вы писали:
Z>>обратить внимание на defined in header... иначе да -- нет header'a -- не будет определения %)
Z>>странно что такой вопрос вообще возник... как ты себе представляешь, чтобы какие-то сущности появлялись "из ниоткуда"... это же не JS какой-нибудь %) Z>>здесь максимум предопределено несколько макросов (ну чуть больше после С++14/17) и на вскидку вспоминается еще результат `typeid`, но чтобы с ним работать адекватно также нужно подключить `typeinfo` header.
МР>Ну и что, а может у меня экзотическая реализация компилятора — этот header в самом компиляторе захардкоден — это что не соответствовало бы стандарту?
попытка использовать нечто, чего не было явно объявлено это не валидная программа с точки зрения стандарта -- и если твой компилятор не делает ее невалидной, то он сам становится невалидным %)
Здравствуйте, σ, Вы писали:
>> rvalue) объекты σ>Ничего, что rvalue — это выражения?
Почему бы тебе просто не рассказать, как правильно? Иы бы все оценили. А то ведь искать неточности в высказываниях других всегда легче, чем отвечать на вопросы.
--
Не можешь достичь желаемого — пожелай достигнутого.
МР>В обоих случаях, x это переменная на стеке, т.е. lvalue. Но, МР>в случае g(int& x): МР>x это другое имя для local_x: &x == &local_x МР>а в случае f(int&& x): МР>x это, наверное, тоже имя для временного объекта: &x == временного объекта? МР>А если временный объект не имеет адреса, а возвращается из функции h() через регистр процессора? Как его адрес взять?
Смотри, есть спецификация языка, и есть требования к поведению программы. В данном случае они таковы, что долдно быть возможным получить адреса фактических параметров этих фунций. Каким образом компилятор удовлетворит этим требованиям — это уже личное дело компилятора, точнее, его разработчиков. То ли он скопирует значение из регистра процессора в память, то ли сразу разместит это значение в памяти (RVO, NRVO), то ли еще как-то. Например, в данном конкретном случае, он может вообще никаких объектов нигде не ращмещать — поскольку, в программе сам объект не используется, а используется только его адрес.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, σ, Вы писали:
R>>rvalue объект
σ>Не существует таких сущностей в C++
Тем не менее, термин "prvalue objects" можно встретиь в стандарте. Саттер, Мейерс и Александреску не стеняются пользоваться этим термином. И понять, что имеется в виду, вполне можно, если не ставить себе целью бессмысленное буквоедство.
--
Не можешь достичь желаемого — пожелай достигнутого.
zaufi:
Z>Переменная "на стэке" (т.е. в памяти) уже само по себе означает что у нее есть адрес -- т.е. с точки зрения C/C++ его можно "взять".
Правила C++ применяются к адресам в воображаемой памяти абстрактной машины, которая представляет собой чисто умозрительную модель вычислений. Задача компилятора C++ — преобразовать исходную программу на C++ в программу, понятную среде исполнения и выдающую некие результаты, которые могли бы получиться при выполнении исходной программы на абстрактной машине, следуя описанным в стандарте правилам. От преобразованной программы не требуется, чтобы она работала с доступной ей памятью точно так же, как это делает абстрактная машина со своей воображаемой памятью. Таким образом, между адресами в рамках абстрактной машины и адресами, которые использует процессор, может не быть никакой связи.
Z>Даже если это параметр функции (которые по соглашению передаются через стэк), внутри вызванной функции у него будет адрес (и да, его можно "взять", какого бы типа он там ни был в С/С++). Если компилятор хотел было оптимизировать эту функцию (допустим она не экспортируемая) и передавать в нее параметр через регистр, но оказалось, что внутри "кто-то берет адрес у параметра", то компилятор сделает так, чтобы это было осуществимо... т.е. просто не станет оптимизировать передачу этого параметра %)
Само по себе взятие адреса у параметра функции ни к чему особенному реализацию не обязывает, она вполне может использовать регистр для хранения значения данного параметра или вообще преобразовать алгоритм так, что от данного параметра и следа не останется.
Z>"временные" (или более обще rvalue) объекты в С/С++, на уровне машинных команд это
Временные объекты в C++ — это абстракция ЯП высокого уровня, единственное предназначение которой — быть частью правил, определяющих observable behavior, ни больше ни меньше. Что там будет на уровне машинных команд, зависит от того, как компилятор захочет реализовать допустимое с точки зрения правил C++ observable behavior.
Z>Возмем `f(h())` в твоем примере. Результат `h()` вероятнее всего просто "перекочует" из регистра (где по соглашению `h()` его должен будет вернуть) сразу в стэк как параметр для `f()`.
Это всё гадание на кофейной гуще. Компилятор может встроить оба вызова, и тогда от соглашений по вызову функций ничего не зависит. А если и не встроит, передача аргумента тоже может быть сделана через регистр, как и возврат значения.
Z>Если ты "сохранишь" этот "промежуточный" результат в "переменную", компилятор вероятнее всего, даже не будет для нее выделять место в стэке, если единственное использование такой переменной это тутже быть переданной в `f(tmp)`. Но как только ты возмешь адрес у этой переменной, компилятор будет вынужден/обязан "дать" ей место в стэке (для автоматических переменных). Просто потому, что у нее должен быть адрес %)
Ещё раз: взятый у объекта C++ адрес — это всего лишь воображаемая абстракция, и компилятор вовсе не обязан резервировать какую-либо адресуемую память под воображаемые объекты с воображаемыми адресами. Проанализировав, как полученный адрес используется в дальнейшем, компилятор в некоторых случаях может изменить алгоритм так, что адресуемая память для получения требуемого правилами C++ результата работы программы не понадобится.
R>>>rvalue объект
σ>>Не существует таких сущностей в C++
R>Тем не менее, термин "prvalue objects" можно встретиь в стандарте.
Это не термин. Это словосочетание. Которое можно встретить всего 1 раз. В non-normative сноске.
И это не повод так выражаться, это повод закинуть editorial issue: https://github.com/cplusplus/draft/issues
R> Саттер, Мейерс и Александреску не стеняются пользоваться этим термином.
А если бы они не стеснялись головой в унитаз макаться — ты бы тоже макался?