0

Why is that I can assign to a temporary value of a class type?

#include<complex>

std::complex<double> f() {return 0.;}
double               g() {return 0.;}

int main() {
    f() = 0.;  // compiles !?
    g() = 0.;  // doesn't compile
}

https://godbolt.org/z/3fx9d5Yac

I think that the builtin behavior is the more natural.

Is this a defect on the way assignment is implemented is implemented in certain classes?

What can be done to the class definition (assuming I have control over the class) to prevent this unexpected behavior and behave like builtins? (assuming one can control the class)

In my experiments adding const to the result type solves the problem, but this is uncostumary and also prevent moves AFAIK, e.g. for larger objects.

const std::complex<double> f() {return 0.;}

One can argue whether the behavior is correct in the language, however it creates very misleading interfaces.

class complex_arg {
    double r_;
    double phi_;
 public:
    std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};

int main() {
    complex_arg ca;
    ca.value() = std::complex<double>(1000., 0.);  // accepted by the compiler
    assert( ca.value() != std::complex<double>(1000., 0.) ); // what!
}

I see only one way out, but it requires modifying the class and it doesn't scale well (to large classes that can be moved).

    const std::complex<double> value() const
alfC
  • 14,261
  • 4
  • 67
  • 118
  • 3
    compiles? The link says that both do not compile. Both lines give an error message, don't they? – Thomas Weller May 19 '22 at 17:07
  • 2
    What is the purpose of `using T = ...` in this example? – mkrieger1 May 19 '22 at 17:08
  • @ThomasWeller That’s due to an unrelated typo. – Konrad Rudolph May 19 '22 at 17:09
  • You could always add a deleted assignment operator: `struct Foo { Foo& operator=(double) && = delete; }; Foo f2() { return {}; }` This way `f2() = 1.;` results in a compiler error. – fabian May 19 '22 at 17:17
  • @ThomasWeller, I sent the wrong link https://godbolt.org/z/3fx9d5Yac , `f() = ...;` compiles with clang at least. maybe it is a clang-related question. – alfC May 19 '22 at 17:31
  • @mkrieger1, removed `using T = ...` it was a leftover. – alfC May 19 '22 at 17:31
  • @fabian, thanks, that means that legacy classes will never behave like built-ins in this regard. On the other hand maybe the built-ins are the odd ones here, because I can think of generating a value, assigning to it (e.g. partially/efficiently) and then passing that to something else. Perhaps the problem is that `Foo& operator=(double) && {...normal impl...}` is not `[[nodiscard]]` at the end? – alfC May 19 '22 at 17:34
  • @alfC The behavior is as expected. We cannot assign to a built in type rvalue. See [assigning to rvalue: why does this compile?](https://stackoverflow.com/questions/33784044/assigning-to-rvalue-why-does-this-compile) or even better [Assigning Rvalue returned from function to another Rvalue](https://stackoverflow.com/questions/35378090/assigning-rvalue-returned-from-function-to-another-rvalue) – Jason May 19 '22 at 17:37
  • @AnoopRana, ok, but it creates very misleading interfaces. See my edit. – alfC May 20 '22 at 03:51
  • @AnoopRana, my pleasure, https://stackoverflow.com/questions/72314747/what-can-be-done-to-prevent-misleading-assigment-to-returned-value – alfC May 20 '22 at 06:54

0 Answers0