LINUX.ORG.RU

p=&(i++) все же возможно, но есть тонкие моменты


0

0

Недавно в Development был вопрос, на который похоже ответили не до конца

Вот пример для g++ 4.1.2. Я его чуть прокомментирую, и прошу знатоков стандарта меня подправить.

#include <iostream>

int main()
{
    int i=1;
    // const int* pi=&(i++);
    const int& j=i++;
    const int* pi=&j;
    std::cout << "i=" << i << " j=" << j << " pi=" << pi << " *pi=" << *pi << '\n';
    return 0;
}

1. const int& j=i++; похоже по стандарту (у Алены-срр видел похожее)

2. const int* pi=&j; мне кажется разумно

3. const int* pi=&(i++); — а вот это непонятно, почему компилятор отвергает

Создавая константный указатель на временный обьект компилятор не должен продлевать время жизни обьекта.

placement_new ★★
()
Ответ на: комментарий от placement_new

>Создавая константный указатель на временный обьект компилятор не должен продлевать время жизни обьекта.

в отличие от ссылки

jtootf ★★★★★
()

После ковыряния в книгах и стандарте, теперь сам могу вам прокоментировать ваш пример. Если ошибаюсь поправте:

>> 1. const int& j=i++; похоже по стандарту (у Алены-срр видел похожее)
Да по стандарту, ибо в при присваивании константной ссылке создается временная переменная, на неё собственно ссылка и будет указывать.

>> 2. const int* pi=&j; мне кажется разумно
Действительно разумно, ибо нет причин по которым нельзя взять адресс lvalue к которому привязана предыдущая ссылка

>> 3. const int* pi=&(i++); -- а вот это непонятно, почему компилятор отвергает
Постфиксный инкремент СНАЧАЛА ВОЗВРАЩАЕТ ТЕКУЩЕЕ ЗНАЧЕНИЕ ПЕРЕМЕННОЙ, А ПОТОМ ЕЁ ИНКРЕМЕНТИРУЕТ. Текущее значение переменной вообще говоря lvalue не является и не должно им являться. Если вдаваться в детали того потока ассемблерного сознания которое генерирует ГЦЦ, то это промежуточное значение хранится не в памяти а в регистве роцессора. Так вот результат инкремента пишется обратно в память а это промежуточное значение участвует в предназначенной ему операции и в таком случае оно вообще не имеет адреса! (что жутко логично). А в стандарте сказано, что постфиксный инкремент не lvalue, а сколь оно логично - наверное логично, ибо иначе вы получите адрес/ссылку временного объекта.

По ходу дела еще вопрос: А как старые версии С переваривали такое:
register int i;
int* prt;

ptr = &i;
i вроде как в регисторе процессора обязана обитать и какой у неё тогда адресс? (Честно не помню боло ли register когда либо строгим предписанием...)

mskmsk1985
()
Ответ на: комментарий от mskmsk1985

register - это не строгое предписание. Компилятор попытается создать код, в котором значение такой переменной будет по возможности кешироваться в регистре процессора при использовании в циклах или просто частом обращении к ее значению. Другой вопрос - легко ли обмануть GCC, написав C-код, неявно изменяющий регистровую переменную, в которую он действительно "поверил" и решил хранить в регистре.

mannaz
()
Ответ на: комментарий от mskmsk1985

> Если вдаваться в детали того потока ассемблерного сознания которое генерирует ГЦЦ, то это промежуточное значение хранится не в памяти а в регистве роцессора.

В недрах gcc код многократно преобразуется из одного представления в другое, многократно по-всякому оптимизируется, и только ближе к концу длинной цепочки трансформаций происходит распределение регистров. Смотреть на ассемблерный выхлоп бесполезно, а временная переменная запросто могла оказаться засунутой в стек (если она долго не нужна, а регистров не хватает), могла не создаваться вообще (если компилятор сумеет понять, что ее в итоге не используют), или даже могла быть вычислена многократно на разных участках ассемблерного кода (если вычислить ее заново - проще, чем возиться со стеком).

Да, и поведение gcc влёгкую меняют от версии к версии, если изменения не ухудшают его совместимость со стандартом. Так что слепо полагаться на реакцию gcc - чревато. Сегодня твой код работает с ним как надо, а завтра - разработчики gcc заявят, что твой код - плохой, и работал он только счастливой случайности.

Manhunt ★★★★★
()
Ответ на: комментарий от alexsaa

>const int& j=i; i++; >даёт одно, а

Тут j ссылается на ячейку памяти, в которой лежит переменная i.

> const int& j=i+0; i++; > даёт другое :)

Тут j ссылается на ячейку памяти, в которой лежит временная переменная, являющаяся суммой i и нуля.

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

Я всегда полагал, что i+0 - это просто такое выражение, которое не lvalue. Тогда получается, что смысл инициализации ссылки зависит от того, что в правой части: конструкция int&j = lvalue - это инициализация указателя, а int&j = rvalue - это просто создание новой переменной?

alexsaa
()
Ответ на: комментарий от Manhunt

> Тут j ссылается на ячейку памяти, в которой лежит временная переменная

Непонятно. В ячейке не может лежать переменная. В ячейке может лежать число. Число не может быть временным. "Временной" может быть сама ячейка (в стеке). Но тогда это - опять же - ничем не отличается от того, что просто создаётся автоматическая переменная j с инициализацией?

alexsaa
()
Ответ на: комментарий от alexsaa

А что именно то не понятно? i - это одна переменная, результат i+0 операцией сложения обратно в i не помещается, просто создается временная переменная которая содержит значение результата если хотите. И вообще эта временная переменная не lvalue и попытка взять ссылку на этот результат приводит к "созданию ссылки на временный объект".

mskmsk1985
()
Ответ на: комментарий от mskmsk1985

Поправка для илициализации const ссылки создается своя временная переменная в которую помещается значение результата. В любом случае вы ссылаетесь не на i!

PS: Про register и код который генерирует ГЦЦ я понял. Пример с асмом приводил лишь для иллюстрации того как все может быть реализовано и только! Естесно все может быть реализовано совсем и не так :)

mskmsk1985
()
Ответ на: комментарий от mskmsk1985

"Попытка взять ссылку на не-lvalue" в общем случае заканчивается ошибкой "lvalue required". Значит, дело не в общем правиле "создание в памяти временного объекта" (впервые слышу про временные объекты на уровне самого языка C++), а в конкретном синтаксисе инициализации ссылки (int&).

alexsaa
()
Ответ на: комментарий от alexsaa

Что значит "временные объекты на уровне самого языка C++"? Инициализировать неконстантную ссылку не lvalue нельзя - это факт медицинский. А имелось ввиду то, что при вычислении выражений результаты могут помещаться во временные переменные, которые вполне подходят под роль инициализаторов константных ссылок.

mskmsk1985
()
Ответ на: комментарий от alexsaa

> const int& j=i; i++; даёт одно, а const int& j=i+0; i++; даёт другое :)

Ну так i и i+0 это два одинаковых числа, но два разных адреса -- т.е. две разных ссылки.

Хотя конечно наблюдение интересное.

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от mskmsk1985

> i вроде как в регисторе процессора обязана обитать и какой у неё тогда адресс?

ЕМНИП ж++ сам может сделать переменную, от которой берут адрес, регистровой, но только пока другие части проги не могут обратиться к ней по адресу, а перед тем, как могут, он сбрасывает ее обратно в память.

Это работает на 1 нити, а с несколькими -- ты ССЗБ если не ставишь локи.

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

А собтвенно если register не строгое предписание тогда и базара нет!

mskmsk1985
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.