Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 19:07
Оценка:
Случайно налетел на такой вот спецэффект: gcc в режиме C++23 выдает такую вот ошибку:

http://coliru.stacked-crooked.com/a/eb3fe810d33f1ebb

int& lvalue(int&& i)
{
    return i; // error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
}


Полез в стандарт. Там всё по-прежнему:

https://timsong-cpp.github.io/cppwp/basic.lval#4

In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.


"Спецэффект" устраняется простым добавлением промежуточной ссылки:

http://coliru.stacked-crooked.com/a/56ab7b159e1927e7

int& lvalue(int&& i)
{
    int& r = i; // OK
    return r;
}


А теперь, внимание, вопрос: что это за хрень?
--
Справедливость выше закона. А человечность выше справедливости.
Re: Reference binding in C++23 - bug of gcc?
От: Великий Реверс google
Дата: 31.01.25 20:17
Оценка:
я мысленно представил что там не int
а какой то объект
он перемещается на стек в функцию
и потом возвращается ссылка со стека?

чет как то бред
так что я согласен с гцц

зы лучше на годболте смотреть и в разных компилерах и версиях
Re[2]: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 20:26
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>я мысленно представил что там не int

ВР>а какой то объект
ВР>он перемещается на стек в функцию
ВР>и потом возвращается ссылка со стека?

ВР>чет как то бред

ВР>так что я согласен с гцц

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

Если тебе мало того, что написано в стандарте, просто скомпилируй этот же код в режиме C++20. А теперь согласен? С каким из двух гцц ты "согласен"?

Ну и, наконец, как ты сам для себя объясняешь, что тот же самый гцц в режиме С++23 спокойно компилирует второй второй пример?

А еще неплохо бы понять, почему мне пришлось всё повторять второй раз. У тебя с чтением какие-то проблемы?
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 31.01.2025 20:30 rg45 . Предыдущая версия .
Re[2]: Reference binding in C++23 - bug of gcc?
От: cserg  
Дата: 31.01.25 20:36
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>зы лучше на годболте смотреть и в разных компилерах и версиях

x86-64 clang 19.1.0 на годболте тоже ошибку выдает для -std=c++23, -std=c++20 без ошибки. Наверно, что-то в стандарте поменяли.
Re[2]: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 20:37
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>зы лучше на годболте смотреть и в разных компилерах и версиях


На годболте всё то же самое. В режиме С++20 компилирует: https://gcc.godbolt.org/z/PrP3YY963, а в режиме С++23 нет: https://gcc.godbolt.org/z/rqEhn478q
--
Справедливость выше закона. А человечность выше справедливости.
Re[3]: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 20:45
Оценка:
Здравствуйте, cserg, Вы писали:

C>Наверно, что-то в стандарте поменяли.


Всё это выглядит очень странно. В стандартах всех версий за последние несколько лет ясно написано: "named rvalue references are treated as lvalues". В то время как компилятор говорит: "cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'". Где он там нашел rvalue of type 'int'? И почему во втором случае тот же самый биндинг проходит без проблем?
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 31.01.2025 20:48 rg45 . Предыдущая версия .
Re[3]: Reference binding in C++23 - bug of gcc?
От: Великий Реверс google
Дата: 31.01.25 20:50
Оценка:
Здравствуйте, rg45, Вы писали:

...

R>Надо понимать, что ты в первые для себя открыл, что имена rvalue ссылок являются lvalue выражениями? Ну, с приехалом тебя. Лучше поздно, чем никогда.


смешно че
нет не открыл
я для себя запомнил правило по возвращению ссылок
если получаем левую ссылку как аргумент, то вернуть можем ее только как левую
если получаем правую ссылку как аргумент, то вернуть можем ее только как правую

...
Re: Reference binding in C++23 - bug of gcc?
От: Великий Реверс google
Дата: 31.01.25 20:56
Оценка: 24 (2)
хз может что то здесь
как раз в С++23 изменили
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.html
Re[2]: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 21:08
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>хз может что то здесь

ВР>как раз в С++23 изменили
ВР>https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.html

А вот это похоже на то место, откуда растут ноги. Похоже, всё сводится к выражению под return. Вот такой пример также не компилится:

http://coliru.stacked-crooked.com/a/dac55f17f3fcc212

int& lvalue(int&& i)
{
    int&& r = std::forward<int>(i);
    return r; // error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
}


Блин, но это же прямое противоречие к "named rvalue references are treated as lvalues". Особенно выхлоп компилера.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 31.01.2025 21:10 rg45 . Предыдущая версия .
Re[4]: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 21:12
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>если получаем левую ссылку как аргумент, то вернуть можем ее только как левую

ВР>если получаем правую ссылку как аргумент, то вернуть можем ее только как правую

Это какие-то догмы, а не правила. Время жизни временного объекта гарантируется до конца полного выражения и rvalue-lvalue преобразование не противоречит ровным счётом ни чему. А пользу принести может.

Например, вывод в файл может быть сделан одним выражением, без заведения дополнительных локальных переменных:

    lvalue(std::ofstream("data.json")) << data;
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 31.01.2025 21:18 rg45 . Предыдущая версия . Еще …
Отредактировано 31.01.2025 21:16 rg45 . Предыдущая версия .
Re[3]: Reference binding in C++23 - bug of gcc?
От: cserg  
Дата: 31.01.25 21:21
Оценка: 9 (1)
Здравствуйте, rg45, Вы писали:

ВР>>https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.html

R>А вот это похоже на то место, откуда растут ноги.
Да, пункт 5.1 как раз ваш случай, код который ломается из-за изменений:
std::istream& getline(std::istream&& in, ~~~) {
    ~~~
    return in;
}
// was changed to

std::istream& getline(std::istream&& in, ~~~) {
    ~~~
    return static_cast<std::istream&>(in);
}
Re[4]: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 31.01.25 21:51
Оценка:
Здравствуйте, cserg, Вы писали:

C>Да, пункт 5.1 как раз ваш случай, код который ломается из-за изменений:

C>
C>std::istream& getline(std::istream&& in, ~~~) {
C>    ~~~
C>    return in;
C>}
C>// was changed to

C>std::istream& getline(std::istream&& in, ~~~) {
C>    ~~~
C>    return static_cast<std::istream&>(in);
C>}
C>


А вообще, выглядит очень костыльно эта "фича", с моей точки зрения. Никто не будет выписывать эти статик касты, а будут писать вот так:

    return *&in;


Они ведь не собираются отменять правило "named rvalue references are treated as lvalues", как я понял. Так кого и от чего они спасают этими костылями? Лучше бы оставили более строгие и менее противоречивые правила.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 31.01.2025 21:52 rg45 . Предыдущая версия .
Re: Reference binding in C++23 - bug of gcc?
От: rg45 СССР  
Дата: 01.02.25 11:10
Оценка: 2 (1)
R>А теперь, внимание, вопрос: что это за хрень?

Резюме.

Главное изменение вот (выделено):

https://timsong-cpp.github.io/cppwp/expr.prim.id.unqual#3

The expression is an xvalue if it is move-eligible (see below); an lvalue if the entity is a function, variable, structured binding, data member, or template parameter object; and a prvalue otherwise ([basic.lval]); it is a bit-field if the identifier designates a bit-field.


"Move-eligible" — новый термин, который является синонимом ранее существовавшего термина "implicitly movable entity". В С++23 они просто перетащили это определение из "Copy/move elision" в "Expressions":

https://timsong-cpp.github.io/cppwp/expr.prim.id.unqual#4

An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type.
In the following contexts, an id-expression is move-eligible:


Так что, гцц опять молодец

P.S. А что нам даёт это изменение? Всё очень просто.

Раньше нужно было писать так (псевдокод):

template <typename T>
T&& foo(T&& t)
{
   modify(t);
   return std::forward<T>(t);
}


Теперь можно писать проще:

auto&& foo(auto&& t)
{
   modify(t);
   return t;
}
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 01.02.2025 16:01 rg45 . Предыдущая версия . Еще …
Отредактировано 01.02.2025 14:01 rg45 . Предыдущая версия .
Отредактировано 01.02.2025 11:27 rg45 . Предыдущая версия .
Отредактировано 01.02.2025 11:26 rg45 . Предыдущая версия .
Отредактировано 01.02.2025 11:26 rg45 . Предыдущая версия .
Отредактировано 01.02.2025 11:17 rg45 . Предыдущая версия .
Отредактировано 01.02.2025 11:14 rg45 . Предыдущая версия .
Отредактировано 01.02.2025 11:13 rg45 . Предыдущая версия .
Re: Вопрос к Shmj.
От: rg45 СССР  
Дата: 02.02.25 11:04
Оценка: :))
R>[skipped]
R>А теперь, внимание, вопрос: что это за хрень?

Shmj, обращаюсь к тебе как к опытному пользователю нейросетей. Мне хотелось бы получше прочувствовать возможности нейросетей в решении практических вопросов на примере данного конкретного случая.

Я попробовал задать этот вопрос копилоту, прямо в том виде, в котором он задан в стартовом сообщении, лишь слегка адаптировав финальную фразу под аутизм искусственного интеллекта. Вот, какой ответ я получил:

Это интересный вопрос! Ваш пример демонстрирует, что в GCC в режиме C++23 возникла ошибка, которая не соответствует текущему стандарту языка C++. Стандарт действительно гласит, что rvalue-ссылки должны быть преобразованы в lvalue (далее следует ссылка на гугл с каким-то совершенно левым ключом для поиска).

Похоже, что это может быть багом в компиляторе GCC. Вы уже нашли рабочее решение, добавив промежуточную ссылку.

Может быть стоит сообщить разработчикам GCC об этой проблеме, чтобы они могли её рассмотреть и исправить.

Если у вас есть ещё вопросы или нужна помощь, дайте знать!


Так вот, отсюда проистекает мой интерес: способны ли нейросети, находящиеся в открытом доступе, найти ответ на этот вопрос? Если да, то насколько сложным должен быть запрос для этого?
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 02.02.2025 14:16 rg45 . Предыдущая версия . Еще …
Отредактировано 02.02.2025 14:03 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 14:01 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 13:51 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 12:00 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 11:34 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 11:31 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 11:28 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 11:24 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 11:21 rg45 . Предыдущая версия .
Отредактировано 02.02.2025 11:19 rg45 . Предыдущая версия .
Re: Reference binding in C++23 - bug of gcc?
От: Великий Реверс google
Дата: 06.02.25 23:52
Оценка:
https://www.sandordargo.com/blog/2024/01/31/cpp23-likes-to-move-it

The original wording is rather complex as it’s also claimed in the abstract of P2266R3. The proposal fixes the mentioned defect and also simplifies the specification by saying that a returned move-eligible id-expression is always an xvalue.


In C++, an eXpiring value (xvalue in short) represents an expiring or about-to-expire value. It typically occurs in the context of a move operation or when the value is no longer needed in its current location. Examples include the result of std::move() and operator[] applied to an rvalue.

вот для чего это было сделано
Re[2]: Вопрос к Shmj.
От: Videoman Россия https://hts.tv/
Дата: 09.02.25 19:46
Оценка: 10 (1) :)
Здравствуйте, rg45, Вы писали:

R>Так вот, отсюда проистекает мой интерес: способны ли нейросети, находящиеся в открытом доступе, найти ответ на этот вопрос? Если да, то насколько сложным должен быть запрос для этого?


Я задал этот вопрос DeepSeek, возможно не найдешь ничего интересного, но её терзания и метания мне понравились, поэтому приведу рассуждения тут:
Вопрос:
Почему следующий код на С++ не компилируется в 23-м стандарте и как его исправить не меняя сигнатуру функции?
int& lvalue(int&& i)
{
return i;
}
Её рассуждения:

Хмм, у пользователя есть код на C++, который не компилируется в стандарте C++23. Нужно разобраться, почему и как это исправить, не меняя сигнатуру функции. Давай посмотрим на код:

int& lvalue(int&& i)

{

return i;

}

Итак, функция принимает rvalue-ссылку на int и возвращает ссылку на int. Внутри возвращается переданный параметр i.

Первое, что приходит в голову: возврат ссылки на rvalue-параметр. Потому что параметр i здесь является rvalue-ссылкой, но внутри функции он является lvalue, так как имеет имя. Возвращая ссылку на него, мы можем получить висячую ссылку, если объект был временным. Но компилятор обычно предупреждает об этом, но не ошибка компиляции, если только не включены определённые флаги.

Однако, возможно, в C++23 ужесточили правила, связанные с привязкой rvalue-ссылок к lvalue. Но почему код не компилируется?

Wait, проблема может быть в том, что нельзя вернуть lvalue-ссылку на rvalue-ссылку. Но нет, функция возвращает int&, а параметр — int&&. Внутри функции параметр i является lvalue, поэтому можно взять его адрес и вернуть как ссылку. Но если вызвать эту функцию с временным объектом, то возвращаемая ссылка будет висячей, потому что временный объект уничтожится после выражения. Но это runtime проблема, а не ошибка компиляции.

Так почему тогда код не компилируется в C++23? Может быть, из-за изменений в стандарте, связанных с обязательным соблюдением правил lifetime или чего-то подобного. Например, в C++23 могла появиться новая фича, которая предотвращает возврат ссылки на параметр, который является rvalue-ссылкой, чтобы избежать висячих ссылок.

Или, возможно, дело в том, что возврат lvalue-ссылки из функции, принимающей rvalue-ссылку, теперь считается ошибкой, так как это может привести к непреднамеренному продлению жизни временного объекта, что небезопасно.

Но почему компилятор это ловит? Может быть, в C++23 добавили более строгие правила для привязки ссылок. Или, возможно, проблема связана с тем, что возвращаемая ссылка имеет тип int&, а не int&&. Но в данном случае функция возвращает lvalue-ссылку, а параметр i внутри функции — это lvalue (потому что у него есть имя), поэтому возврат int& корректен.

Wait, ошибка компиляции возникает из-за того, что мы пытаемся связать lvalue (i) с возвращаемым типом int&. Но это же допустимо. Возможно, дело в другом.

Проверю: код функции пытается вернуть ссылку на параметр, который является rvalue-ссылкой. Но параметр функции, являющийся rvalue-ссылкой, может быть привязан только к временным объектам. Когда мы возвращаем ссылку на этот параметр, то если параметр был временным объектом, то после завершения функции ссылка становится висячей. Но компилятор обычно не запрещает это на этапе компиляции, только предупреждение, если включены.

Но может быть, в C++23 появились новые правила, которые делают такой код недопустимым. Например, если параметр функции — это rvalue-ссылка, то возврат ссылки на него может быть запрещён, так как это продлевает жизнь временного объекта за пределы функции, что невозможно.

Но как тогда можно исправить код, не меняя сигнатуру функции? То есть сигнатура должна остаться int& lvalue(int&& i). Тогда, возможно, нужно использовать std::move при возврате? Но тогда возвращаемый тип будет int&&, а у нас int&. Не подходит.

Или, возможно, использовать static_cast<int&>? Но i уже является lvalue типа int, поэтому приведение не нужно. Возможно, проблема в другом.

Wait, возможно, проблема в том, что в C++23 запретили неявное преобразование из int&& в int&. То есть, возвращая i, который является lvalue типа int, но параметр функции — это rvalue-ссылка. Но как это влияет на возвращаемое значение?

Нет, тип i внутри функции — это int (так как rvalue-ссылка на int становится lvalue выражения). То есть, возврат int& из lvalue выражения допустим. Возможно, компилятор выдаёт ошибку из-за того, что возвращается ссылка на параметр, который является rvalue-ссылкой, что может привести к висячей ссылке. Но это должно быть предупреждением, а не ошибкой.

Но если код не компилируется, значит, компилятор считает это ошибкой. Может быть, в C++23 добавили правило, что нельзя возвращать ссылку на параметр, переданный как rvalue-ссылка, потому что это опасно. Тогда как исправить?

Один из способов — сделать параметр lvalue-ссылкой. Но сигнатуру менять нельзя. Значит, нужно найти другой способ.

Возможно, проблема в том, что в C++23 требуется явно продлевать время жизни временного объекта, но я не уверен.

Wait, возможно, в C++23 изменили правила привязки возвращаемых ссылок. Например, если функция принимает rvalue-ссылку, то возврат ссылки на этот параметр требует явного преобразования.

Но как тогда вернуть ссылку? Может быть, нужно использовать std::forward?

Но std::forward обычно используется для perfect forwarding. В данном случае, параметр i является rvalue-ссылкой, и внутри функции это lvalue. Если мы хотим вернуть его как rvalue, то нужно использовать std::move, но возвращаемый тип int&, а не int&&.

Wait, тогда std::move(i) приведёт к int&&, но мы пытаемся вернуть int&. Это несовместимо. Так что такой вариант не подходит.

Тогда возможно, что в C++23 код возврата i в данном контексте стал недопустимым. Но почему?

Может быть, проблема в том, что параметр функции — это rvalue-ссылка, и его нельзя привязать к lvalue-ссылке на возврате. То есть, возвращаемый тип — lvalue-ссылка, но исходный объект был передан как rvalue-ссылка. Возможно, теперь требуется явное преобразование.

Но тогда как можно преобразовать int&& в int&? Это разные типы ссылок. Но внутри функции i — это lvalue, поэтому можно взять его адрес. Возможно, компилятор теперь считает, что возврат ссылки на rvalue-параметр является ошибкой, так как это может привести к висячей ссылке.

Если это так, то как исправить? Может быть, добавить static_cast<int&>(i)? Но это избыточно, так как i уже является lvalue типа int.

Или, возможно, проблема в другом. Например, в C++23 изменили правила инициализации возвращаемого значения. Но я не слышал о таких изменениях.

Другой вариант: возможно, код пытается вернуть rvalue-ссылку как lvalue-ссылку, что теперь запрещено. Но на самом деле, внутри функции i — это lvalue, поэтому возврат int& из lvalue допустим.

Возможно, проблема в том, что в C++23 добавили проверку на то, что возвращаемая ссылка привязана к объекту, который переживёт вызов функции. Но как компилятор может это проверить? Например, если функция возвращает ссылку на свой параметр, который является rvalue-ссылкой, то это может быть временным объектом, и тогда возвращаемая ссылка будет висячей. Но компилятор не может знать наверняка, был ли передан временный объект или нет.

Но возможно, в C++23 ввели правило, запрещающее возвращать ссылки на параметры, переданные как rvalue-ссылки. Тогда код становится невалидным.

Если это так, то как можно исправить код, не меняя сигнатуру? Возможно, нужно изменить возвращаемую ссылку на rvalue, но сигнатура требует int&. Значит, нужно найти другой способ.

Wait, может быть, проблема в том, что возвращается ссылка на временный объект, и компилятор теперь это обнаруживает как ошибку. Например, если функция вызывается с временным объектом, например, lvalue(42); тогда возвращаемая ссылка будет ссылаться на уничтоженный временный объект, и компилятор в C++23 может запрещать такой код.

Но тогда это зависит от контекста вызова. Однако сама функция может быть вызвана с lvalue, если использовать std::move. Например:

int x = 5;

int& ref = lvalue(std::move(x));

В этом случае, временный объект — это x, который всё ещё существует, поэтому ссылка будет валидной. Но компилятор не может знать, как будет использоваться функция. Поэтому, возможно, ошибка возникает в самом определении функции.

Возможно, в C++23 стало невозможно возвращать ссылку на параметр функции, если этот параметр является rvalue-ссылкой, из-за новых правил безопасности времени жизни (lifetime safety). Но я не уверен, есть ли такие изменения в стандарте.

Если это так, то как можно обойти эту проблему? Нужно вернуть ссылку, но параметр — rvalue-ссылка.

Один из вариантов — создать копию и вернуть ссылку на неё, но это приведёт к возврату ссылки на локальную переменную, что недопустимо.

Другой вариант — сохранить параметр в статическую переменную, но это тоже плохо и не потокобезопасно.

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

Может быть, проблема в том, что компилятор считает, что параметр i является xvalue, и поэтому его нельзя привязать к lvalue-ссылке. Но внутри функции i — это lvalue, так как у него есть имя. Поэтому возврат int& из i допустим.

Тогда почему код не компилируется?

Возможно, ошибка не связана напрямую с возвратом ссылки, а с другим аспектом. Например, в C++23 изменили правила для rvalue-ссылок.

О, возможно, проблема в том, что функция возвращает non-const lvalue-ссылку на rvalue-ссылку параметра. Но это не должно быть проблемой, так как параметр внутри функции — это lvalue, и его можно привязать к non-const ссылке.

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

Попробую найти информацию об изменениях в C++23, связанных с возвратом ссылок из функций.

В C++23 был принят proposal P2266, который касается безопасности времени жизни (lifetime safety). Возможно, это вводит новые правила, запрещающие возвращать ссылки на параметры, переданные как rvalue-ссылки, если это может привести к висячим ссылкам. Но пока что это, вероятно, только рекомендации, а не обязательные проверки компилятора.

Если компилятор реализует некоторые из этих предложений как расширения, то он может выдавать ошибку. Но тогда это зависит от компилятора и его настроек.

Однако, если код не компилируется в C++23, возможно, из-за того, что теперь rvalue-ссылки не могут быть неявно преобразованы в lvalue-ссылки в возвращаемом значении. Но это маловероятно.

Другой подход: попробовать скомпилировать этот код и посмотреть на ошибку. Допустим, компилятор говорит что-то вроде "возврат ссылки на параметр, переданный как rvalue-ссылка".

Если это так, то исправление может заключаться в том, чтобы сделать параметр lvalue-ссылкой, но сигнатуру менять нельзя. Значит, нужно обойти это как-то.

Возможно, нужно использовать std::forward, но для этого нужно изменить тип возвращаемого значения, что невозможно.

Или использовать приведение типов. Например, привести i к int&. Но i уже является lvalue типа int, поэтому приведение не требуется.

Может быть, нужно добавить ключевое слово std::move, но тогда возвращаемый тип будет rvalue-ссылкой, что не совпадает с сигнатурой функции. Это вызовет ошибку несоответствия типов.

Тогда возникает вопрос: почему код не компилируется. Возможно, из-за того, что в C++23 изменили правила и теперь нельзя возвращать lvalue-ссылку из функции, которая принимает rvalue-ссылку, так как это может продлить жизнь временного объекта. Но это не имеет смысла, потому что временный объект будет уничтожен после вызова функции, и ссылка станет висячей. Но компилятор обычно не запрещает это, если только не включены определённые проверки.

Возможно, пользователь использует флаг компилятора, например, -Werror=return-local-addr, который превращает предупреждение в ошибку. Но это не связано с C++23.

Если же проблема именно в стандарте C++23, то, возможно, компилятор теперь требует явного приведения.

Но как тогда исправить код? Если сигнатура функции должна остаться int& lvalue(int&& i), то нужно вернуть ссылку на i. Возможно, добавить явное приведение:

return static_cast<int&>(i);

Но это не изменит семантики, так как i уже является lvalue.

Или, возможно, проблема в том, что компилятор считает, что возвращаемая ссылка имеет тип, отличный от типа параметра. Но в данном случае, параметр — int&&, а возвращаемое значение — int&. Но внутри функции параметр i является lvalue, поэтому тип выражения i — int&.

Может быть, в C++23 запретили привязывать int&& к int& в возвращаемом значении? Не уверен.

Если всё это не помогает, возможно, стоит проверить код на Compiler Explorer с разными версиями стандартов и посмотреть на ошибку.

Допустим, при компиляции с C++23 возникает ошибка: "cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'". Но это маловероятно.

Или ошибка в том, что возвращаем lvalue-ссылку на объект, который был перемещён. Но здесь нет перемещения.

Возможно, компилятор считает, что параметр i является xvalue, и поэтому нельзя вернуть на него lvalue-ссылку. Но внутри функции i — это lvalue.

В общем, сложно понять без конкретного сообщения об ошибке. Но предположим, что в C++23 код не компилируется из-за того, что возвращается ссылка на rvalue-ссылку параметра. Тогда как исправить?

Если нельзя изменить сигнатуру, то, возможно, нужно использовать std::forward, чтобы сохранить категорию значения. Но функция возвращает int&, поэтому std::forward не поможет, так как это приведёт к rvalue-ссылке.

Возможно, проблема решается добавлением const. Но сигнатура не позволяет.

Или другой вариант: раз параметр является rvalue-ссылкой, то его можно переместить, но это не имеет смысла для int.

В общем, возможно, правильный ответ заключается в том, что в C++23 ужесточили правила, и теперь нельзя возвращать ссылку на параметр, переданный как rvalue-ссылка. Чтобы исправить, нужно использовать std::move при возврате, но тогда тип возвращаемого значения должен быть int&&, что противоречит сигнатуре. Поэтому это невозможно, и единственный способ — изменить сигнатуру, но пользователь запрещает это.

Значит, возможно, код некорректный, и в C++23 его нельзя скомпилировать без изменения сигнатуры. Но пользователь говорит, что нужно исправить, не меняя сигнатуру. Тогда как?

Может быть, проблема в другом. Например, в C++23 изменили правила инициализации возвращаемого значения для функций, возвращающих ссылки. Например, требуется, чтобы возвращаемое выражение было точно того же типа.

Но int&& нельзя неявно преобразовать в int&. Поэтому, если внутри функции попытаться вернуть int&& как int&, это вызовет ошибку.

Но параметр i внутри функции имеет тип int, но объявлен как int&&. То есть, это rvalue-ссылка, которая инициализирована с временным объектом. Внутри функции i является lvalue типа int. Поэтому возврат int& из i допустим.

Но тогда почему компилятор выдаёт ошибку?

Возможно, в C++23 ввели новое правило, которое запрещает привязывать lvalue-ссылку к объекту, который был передан через rvalue-ссылку. Но это странно, так как rvalue-ссылка в параметре функции может быть привязана как к временным объектам, так и к xvalue (например, через std::move). Если функция возвращает ссылку на такой объект, то в случае временного объекта это приводит к UB, но в случае xvalue это допустимо.

Но компилятор не может знать, как была вызвана функция, поэтому он не может запретить это в общем случае.

Тогда возможно, что проблема не в стандарте C++23, а в настройках компилятора или в конкретной реализации.

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

Возможно, в C++23 код считается некорректным из-за того, что функция возвращает ссылку на параметр, который является rvalue-ссылкой, и это приводит к неопределённому поведению, поэтому компилятор отказывается компилировать.

Тогда как исправить? Нужно сохранить параметр в объект с продлённым временем жизни. Но внутри функции это невозможно, так как при возврате ссылки локальный объект умрёт.

Значит, единственный способ — изменить функцию, чтобы она не возвращала ссылку на параметр. Но тогда как сохранить сигнатуру?

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

Получается, что код изначально содержит потенциальную ошибку (возврат ссылки на временный объект), и в C++23 это стало ошибкой компиляции. Исправить это без изменения сигнатуры невозможно, так как любое исправление потребует изменения возвращаемого типа или параметра.

Но пользователь просит не менять сигнатуру. Тогда, возможно, нужно использовать обходной путь, например, создать static переменную и вернуть ссылку на неё, но это приведёт к возврату ссылки на один и тот же объект для всех вызовов, что может быть нежелательно.

Например:

int& lvalue(int&& i)

{

static int j;

j = i;

return j;

}

Но это изменяет поведение функции: теперь возвращается ссылка на статическую переменную, которая содержит копию значения параметра. Это может быть не то, что нужно, но позволяет сохранить сигнатуру.

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

Тогда, возможно, ответ заключается в том, что код невозможно исправить без изменения сигнатуры, так как возврат ссылки на rvalue-параметр приводит к висячей ссылке, и в C++23 это запрещено. Поэтому нужно изменить сигнатуру, но пользователь запрещает это. Следовательно, код не может быть исправлен.

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

Тогда, возможно, проблема в другом. Например, в C++23 изменили правила вывода типа возвращаемого значения, но в данном случае тип явно указан.

Возможно, нужно использовать явное приведение:

return static_cast<int&>(i);

Но я не уверен, поможет ли это. Однако, если компилятор считает, что i является rvalue, то приведение к lvalue-ссылке может помочь.

Но внутри функции i — это lvalue, так как у него есть имя. Поэтому static_cast здесь избыточен.

Возможно, ошибка возникает из-за того, что в C++23 параметр функции int&& i считается как rvalue внутри функции, и попытка вернуть его как lvalue-ссылку невозможна. Но это противоречит основам C++, так как любой именованный rvalue-ссылочный параметр является lvalue.

Да, это ключевой момент. Внутри функции, параметр, который является rvalue-ссылкой, сам по себе является lvalue. Потому что у него есть имя. Поэтому возврат ссылки на него (как int&) должен быть разрешён. То есть, код должен компилироваться.

Тогда почему он не компилируется в C++23?

Возможно, пользователь использует компилятор, который имеет баг или ещё не полностью поддерживает C++23. Либо это ошибка в самом коде, не связанная с стандартом.

Но раз пользователь говорит, что проблема в стандарте C++23, нужно искать изменения в нём.

Проверю документацию C++23 на предмет изменений, связанных с возвратом ссылок из функций.

Одно из изменений в C++23 — разрешение возвращать auto(x) для ссылок. Но это, вероятно, не относится к данной проблеме.

Другое изменение — attributes on lambda-expressions, что тоже не связано.

Возможно, проблема связана с тем, что в C++23 сделан более строгим правило, запрещающее привязку rvalue к non-const lvalue-ссылкам. Но это не так, потому что внутри функции i — это lvalue.

Или, возможно, проблема в том, что в C++23 параметр функции int&& i рассматривается как rvalue внутри функции, что неверно.

Нет, это не так. Параметр функции, объявленный как rvalue-ссылка, является lvalue внутри функции. Это базовое правило C++11 и более новых стандартов.

Следовательно, код должен компилироваться. Тогда почему пользователь говорит, что он не компилируется в C++23?

Возможно, это ошибка в компиляторе. Или пользователь допустил опечатку в коде. Например, забыл вернуть значение или что-то ещё. Но в предоставленном коде ошибок нет.

Тогда, возможно, пользователь использует флаг -Werror=return-local-addr или аналогичный, который превращает предупреждение в ошибку. Например, компилятор может предупреждать, что возвращается ссылка на параметр, который является rvalue-ссылкой, что может привести к висячей ссылке. Если этот флаг включён, то компиляция завершится с ошибкой.

Но это не связано с C++23, а зависит от настроек компилятора.

В таком случае, чтобы исправить код, можно подавить предупреждение или использовать обходной путь, например, обернуть возвращаемое значение в [maybe_unused]] или что-то подобное, но это вряд ли поможет.

Или же, как вариант, изменить код так, чтобы компилятор не видел, что возвращается ссылка на параметр. Но как?

Например, можно вернуть *&i, но это бессмысленно.

Возможно, использование макросов или других конструкций, но это не изменит сути.

В итоге, возможно, правильный ответ заключается в том, что код некорректен, так как возвращает ссылку на временный объект, и в C++23 это стало ошибкой компиляции. Однако, согласно стандарту C++, это не должно быть ошибкой компиляции, а только потенциальным UB. Поэтому исправить код можно, изменив логику, чтобы не возвращать ссылку на параметр, но сигнатуру менять нельзя. Следовательно, код невозможно исправить без изменения сигнатуры.

Но пользователь требует исправить без изменения сигнатуры. Тогда, возможно, правильный ответ — добавить std::move при возврате, но это изменит тип возвращаемого значения на rvalue-ссылку, что несовместимо с сигнатурой. Значит, это не вариант.

Тогда остаётся только одно: код некорректен, и его нельзя исправить без изменения сигнатуры. Но пользователь утверждает, что нужно исправить. Значит, возможно, я что-то упускаю.

Wait, возможно, проблема в том, что в C++23 изменили правило, и теперь параметр функции int&& i является rvalue внутри функции, поэтому его нельзя преобразовать в lvalue-ссылку. Но это противоречит основным правилам C++. Поэтому такого быть не может.

Скорее всего, пользователь столкнулся с ошибкой компилятора или неправильно интерпретирует сообщение об ошибке. Либо код компилируется с предупреждением, но не с ошибкой.

Тогда, как исправить код? Например, чтобы избежать предупреждения, можно использовать явное приведение:

return static_cast<int&>(i);

Но это не изменит поведения, просто сделает приведение явным.

Или использовать макрос для подавления предупреждения, но это плохая практика.

В итоге, наиболее вероятный ответ: код возвращает ссылку на параметр, который является rvalue-ссылкой. В C++23 это может привести к ошибке компиляции из-за новых правил безопасности времени жизни. Чтобы исправить, можно использовать std::forward, но это не подходит из-за сигнатуры. Поэтому единственный способ — изменить сигнатуру, что запрещено. Следовательно, код не может быть исправлен без изменения сигнатуры.

Но это противоречит условию вопроса. Поэтому, возможно, правильный ответ заключается в том, что код на самом деле корректен в C++23, и проблема в другом. Но если пользователь утверждает, что код не компилируется, возможно, он использует специфический компилятор или флаги.

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

Значит, остаётся только одно: код содержит неявное преобразование из int&& в int&, которое в C++23 стало явным. Поэтому нужно добавить явное приведение:

return static_cast<int&>(i);

Но это лишь делает приведение явным, что, возможно, устранит ошибку компиляции.

Проверю это:

int& lvalue(int&& i)

{

return static_cast<int&>(i);

}

Такой код должен быть корректен, так как i является lvalue внутри функции, и приведение к int& допустимо.

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

Таким образом, ответ: в C++23 ужесточены правила, и требуется явное приведение типа. Исправить код можно, добавив static_cast<int&>(i) в return.


Ответ:
Код не компилируется в C++23, потому что возврат ссылки на параметр, объявленный как int&&, может привести к висячей ссылке, если параметр был временным объектом. Внутри функции параметр i является lvalue, но в C++23 ужесточены правила безопасности времени жизни, что делает такой возврат недопустимым без явного подтверждения намерения.

Исправление:
Добавьте явное приведение типа через static_cast, чтобы преобразовать lvalue в ссылку. Это подтверждает, что вы осознанно возвращаете ссылку на объект, переданный как rvalue.

cpp
Copy
int& lvalue(int&& i)
{
return static_cast<int&>(i);
}
Объяснение:

Параметр i внутри функции является lvalue (так как имеет имя), поэтому его можно явно привести к int&.

static_cast<int&>(i) подчёркивает намерение вернуть lvalue-ссылку, обходя новые проверки компилятора в C++23.

Сигнатура функции остаётся без изменений.
Re[3]: Вопрос к Shmj.
От: rg45 СССР  
Дата: 10.02.25 14:10
Оценка: :)
Здравствуйте, Videoman, Вы писали:

V>Я задал этот вопрос DeepSeek, возможно не найдешь ничего интересного, но её терзания и метания мне понравились, поэтому приведу рассуждения тут:

V> . . .

Чувствуется, что Shmj с DeepSeek уже поработал
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 10.02.2025 14:16 rg45 . Предыдущая версия .
Re[2]: Вопрос к Shmj.
От: Shmj Ниоткуда  
Дата: 16.02.25 08:13
Оценка: 11 (2)
Здравствуйте, rg45, Вы писали:

R>Shmj, обращаюсь к тебе как к опытному пользователю нейросетей. Мне хотелось бы получше прочувствовать возможности нейросетей в решении практических вопросов на примере данного конкретного случая.


R>Я попробовал задать этот вопрос копилоту, прямо в том виде, в котором он задан в стартовом сообщении, лишь слегка адаптировав финальную фразу под аутизм искусственного интеллекта. Вот, какой ответ я получил:


Мне оповещения не приходят, по этому если меня где-то упомянут — то не всегда увижу. Если вообще такие оповещения тут есть.

По сути вопроса. Вам нужна точность, соответствие стандарту — а с этим у ИИ плохо. Можно сказать что его ответ — просто как мозговой штурм в таких вопросах, когда люди собираются и высказывают разные бредовые варианты решения, а вдруг кого-то это натолкнет на мысль:

Поведение, которое вы наблюдаете, связано с тем, как C++ обрабатывает ссылки на rvalue, особенно когда они используются в операторе return.

В вашем исходном коде:

int& lvalue(int&& i)
{
    return i; // ошибка
}


Параметр i — это именованная rvalue-ссылка (int&&). Согласно стандарту C++, именованные rvalue-ссылки рассматриваются как lvalue. Таким образом, i является lvalue типа int&&.

Однако при использовании i в операторе return вступает в силу особое правило. Согласно стандарту C++ ([class.copy.elision]/3):

В операторе return, когда операнд является (возможно, заключённым в скобки) идентификатором, обозначающим объект с автоматическим временем жизни и типом T (игнорируя cv-квалификацию верхнего уровня), этот объект рассматривается как xvalue.


Это означает, что в return i;, несмотря на то, что i является именованной переменной, она рассматривается как xvalue (истекающее значение), так как это rvalue-ссылка, передаваемая в return.

Ссылка на lvalue типа int& не может привязываться к xvalue; она может привязываться только к lvalue. Поэтому компилятор выдаёт ошибку — он не может привязать int& к xvalue i.

С другой стороны, если добавить промежуточную ссылку:

int& lvalue(int&& i)
{
    int& r = i; // OK
    return r;
}


Здесь i используется для инициализации r. В этом контексте i рассматривается как lvalue типа int&&, и привязка ссылки происходит по стандартным правилам C++.

Хотя может показаться, что привязка int& к int&& должна быть недопустимой, язык C++ позволяет связывать lvalue-ссылку с lvalue совместимого типа, даже если это rvalue-ссылка. Главное различие в том, что переменная i используется в обычном присваивании, а не в return, поэтому специальное правило, превращающее её в xvalue, не применяется.

Итог:

=сначала спроси у GPT=
Re[3]: Вопрос к Shmj.
От: rg45 СССР  
Дата: 16.02.25 08:23
Оценка:
Здравствуйте, Shmj, Вы писали:


S>По сути вопроса. Вам нужна точность, соответствие стандарту — а с этим у ИИ плохо. Можно сказать что его ответ — просто как мозговой штурм в таких вопросах, когда люди собираются и высказывают разные бредовые варианты решения, а вдруг кого-то это натолкнет на мысль:


S>

S>Итог:

S>

    S>
  • В return i; переменная i рассматривается как xvalue, а ссылка int& не может быть привязана к xvalue, поэтому возникает ошибка.
    S>
  • В int& r = i; переменная i рассматривается как lvalue, и привязка разрешена.
    S>
  • Добавление промежуточной ссылки работает, потому что оно избегает специального правила, которое превращает i в xvalue в операторе return.
    S>


И это правильный ответ! А можно чуть больше подробностей, что за нейросеть, и какой был запрос?
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: Вопрос к Shmj.
От: Shmj Ниоткуда  
Дата: 16.02.25 14:27
Оценка:
Здравствуйте, rg45, Вы писали:

R>И это правильный ответ! А можно чуть больше подробностей, что за нейросеть, и какой был запрос?


Это OpenAI o1 preview. Она чуть лучше китайского deepSeek.

Юзаю через API — там можно платить за каждый запрос, примерно 3 цента стоил ответ на ваш вопрос.

Мне не так уж часто нужно задавать такие вопросы, обычно использую как помощника по документации, как поисковик продвинутый. А для таких сложных вопросов решил держать на счету 5 баксов, чтобы раз 5 в неделю делать более сложные вопросы, чтобы не платить регулярно $25.

> и какой был запрос


Запрос — скопировал твое сообщение, но убрал ссылки.
=сначала спроси у GPT=
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.