Изменение возвращаемого значения основного типа и типа класса

#c

#c

Вопрос:

Есть такой код:

 #include <iostream>
#include <string>

int returnnumber() { return 2; }
std::string returntext() { return "siema"; }

int main() {

    std::cout << (returntext()  = "cze") << std::endl; // siemacze
    //std::cout << (returnnumber()  = 2) << std::endl; error: lvalue required as left operand of assignment

    return 0;
} 
  

Почему можно изменить возвращаемое значение std::string , но не int ?

Ответ №1:

because std::string — это тип класса с определенным = оператором в качестве функции-члена.

и стандарт позволяет вызывать функции-члены для rvalues .

глупым следствием этого является то, что

 struct S { int x; };
S foo() { return S(); }

int main()
{
    foo() = S();    // OK, uses member assignment operator.
    foo().x = 666;  // !Nah, can't assign to rvalue of built-in type.
}
  

результаты компиляции:

Comeau C / C    4.3.10.1 (6 октября 2008 11:28:09) для ONLINE_EVALUATION_BETA2
Авторское право 1988-2008 Comeau Computing. Все права защищены.
РЕЖИМ: строгие ошибки C    C    0x_extensions

"ComeauTest.c", строка 7: ошибка: выражение должно быть изменяемым значением lvalue
foo().x = 666; // !Нет, не могу присвоить rvalue встроенного типа.
 ^

при компиляции "ComeauTest.c" обнаружена 1 ошибка.

однако компиляторы отличаются (или раньше отличались) в отношении того, насколько строго они применяли это тонкое правило, и применяли ли вообще.

приветствия и привет.,

Комментарии:

1. Хорошо, но для int оператора = также определено.

2. @anonymous downvoter: пожалуйста, объясните свой downvote, чтобы другие могли извлечь выгоду из вашего понимания или проигнорировать какую-то глупую причину.

3.@Vlad: for int = — это встроенная operator= функция, в то время как for std::string — это функция-член. Я обновляю свой ответ, чтобы сделать это более понятным. Приветствия и привет.,

4. Интересно, в чем разница: если операторам-членам разрешено применять к rvalues , почему то же самое не разрешено для встроенных операторов?

5. @Vlad: Я не знаю, но по поводу очень похожего вопроса Бьярне Страуструп (разработчик языка) ответил, что это была в основном историческая случайность. Он хотел сесть и решить проблему, но обнаружил, что у него нет времени. Есть много такого, что похоже на это… Приветствия,

Ответ №2:

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

operator = является функцией-членом std::string , и вы можете вызвать функцию-член для rvalue типа class .

Комментарии:

1. Вы имели в виду «левую часть оператора присваивания?»

Ответ №3:

По той же причине

 std::string("siema")  = "cze";
  

работает.

Вы создаете новый объект и применяете к operator = нему (который std::string имеет).

Попытка сделать это с ним не сработает, поскольку ваша функция возвращает an rvalue . Это было бы как:

 2  = 2
  

Вы можете поиграть с этим:

 #include <iostream>
#include <string>

intamp; returnnumber() { int * k = new int; *k = 2; return *k; }
std::string returntext() { return "siema"; }

int main() {

    std::cout << (returntext()  = "cze") << std::endl; // siemacze
    std::cout << (returnnumber()  = 2) << std::endl; //no error
    std::string("siema")  = "cze";
    return 0;
} 
  

Но это приведет к утечке памяти, поэтому не делайте этого. Это просто доказательство концепции, что возврат an lvalue будет работать.

Комментарии:

1. Привет, Лучиан 🙂 Также не returntext возвращает значение rvalue?

2. Чарльз Бейли написал: но возвращаемое значение функции всегда является значением rvalue, если функция не возвращает ссылочный тип. Тогда returntext возвращает rvalue или lvalue?

3. returntext действительно возвращает rvalue типа std::string (3.10 / 5 в C 03). Для встроенных назначений требуется значение lvalue (я полагаю, это правило унаследовано от C, хотя теоретически C мог бы измениться, если бы захотел). Однако функции-члены, включая операторы, перегруженные как члены, могут вызываться для значений rvalues. Поскольку int не имеет никаких функций-членов, тогда string как имеет, эти два правила приводят к несоответствию. В C 11 существует множество других типов *value , я все еще не все понял, поэтому я не буду пытаться ссылаться на это, но эффект тот же.

4. При повторном взгляде мое разъяснение было немного ленивым. Точнее сказать, что returntext возвращает a std::string (и в общем случае функции возвращают либо объекты, либо ссылки), и что выражение returntext() является значением rvalue (которое в данном случае ссылается на объект, возвращаемый вызовом функции). Это потому, что, если быть точным, объекты не являются значениями lvalues или rvalues, l-или-r-ness является свойством выражений.

Ответ №4:

returntext() возвращает a std::string , который может быть изменен на более поздних этапах, скажем, с =operator помощью . Однако, несмотря returnnumber() на то, что возвращает an int , сама функция возвращает 2 значение по умолчанию a const int и не может быть изменена, и именно поэтому компилятор жалуется.