Оператор присваивания — функция C ++ возвращает значение, но что может быть присвоено новое значение?

Код выглядит следующим образом:

 #include <iostream>
using namespace std;

class A {

};

A rtByValue() {
return A();
}

void passByRef(A &aRef) {
// do nothing
}

int main() {
A aa;
rtByValue() = aa;            // compile without errors
passByRef(rtByValue());      // compile with error

return 0;
}

Компилятор g ++ выдает следующую ошибку:

d.cpp: In function ‘int main()’:
d.cpp:19:23: error: invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’
d.cpp:12:6: error: in passing argument 1 of ‘void passByRef(A&)’

Это говорит о том, что я не могу передать rvalue в качестве аргумента неконстантной ссылки, но меня смущает то, почему я могу присвоить этому rvalue, как показывает код.

6

Решение

Передача Rvalue rtByValue() для функции, которая ожидает, что ссылка на lvalue не работает, потому что это потребует инициализации аргумента ссылки на lvalue из rvalue. §8.5.3 / 5 описывает, как ссылки на lvalue могут быть инициализированы — я не буду цитировать это полностью, но это в основном говорит, что ссылка на lvalue может быть инициализирована

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

Поскольку аргумент, который нам нужно инициализировать, не является константной ссылкой, это не применимо.

С другой стороны,

rtByValue() = aa;

то есть присвоение временному объекту возможно из-за:

(§3.10 / 5) lvalue для объекта необходимо для того, чтобы модифицировать объект, за исключением того, что rvalue типа class может также использоваться для изменения его референта при определенных обстоятельствах. [Пример: функция-член, вызываемая для объекта (9.3), может модифицировать объект. — конец примера]

Так что это работает только потому, что A имеет тип класса, и (неявно определенный) оператор присваивания является функцией-членом. (Увидеть этот связанный вопрос для дальнейших деталей.)

(Так что если rtByValue() должны были вернуть, например, int, тогда назначение не будет работать.)

8

Другие решения

Потому что вы можете (но не должны!) Переопределять оператор = так, что имеет смысл вызывать его для значения r. Рассмотрим следующий код:

#include<iostream>

using namespace std;

class foo;

foo* gotAssigned = NULL;
int assignedto = -1;

class foo {
public:
foo(int v) : val(v) {}
foo& operator=(int v) {
assignedto=v;
gotAssigned = this;
val = v;
return *this;
}
int val;
};

foo theFoo(2);

foo returnTheFooByValue() {
return theFoo;
}

main() {
returnTheFooByValue()=5;
cout << "[" << assignedto << "] " << theFoo.val << " versus " << gotAssigned->val << endl;
}

Теперь давайте скомпилируем его несколькими способами:

$ g++ -O0 -o rveq rveq.cc && ./rveq
[5] 2 versus 5
$ g++ -O1 -o rveq rveq.cc && ./rveq
[5] 2 versus 2
$ g++ -O4 -o rveq rveq.cc && ./rveq
[5] 2 versus -1218482176

Я не могу обещать, что вы увидите те же результаты.

Как видите, присваивание происходит, но любая попытка использовать объект, который получил назначенный результат, приводит к поведению, специфичному для реализации.

Кстати, это только применяется к пользовательским типам. Этот код:

int v(){
return 2;
}

main(){
v()=4;
}

не компилируется

1

@ddriver Это выводит число 7, как я и ожидал.

#include <iostream>
using namespace std;

class A {
public:
int i;
A() {i = 0x07;}
};

A rtByValue() {
return A();
}

void passByRef(A &aRef) {
cout << aRef.i;
}

int main() {
passByRef(rtByValue());
return 0;
}
0
По вопросам рекламы [email protected]