There are two distinct forms of “properties” that turn up in the standard library, which I will categorise as “Identity oriented” and “Value oriented”. Which you choose depends on how the system should interact with Foo. Neither is “more correct”.
Identity oriented
class Foo
{
X x_;
public:
X & x() { return x_; }
const X & x() const { return x_; }
}
Here we return a reference to the underlying X member, which allows both sides of the call site to observe changes initiated by the other. The X member is visible to the outside world, presumably because it’s identity is important. It may at first glance look like there is only the “get” side of a property, but this is not the case if X is assignable.
Foo f;
f.x() = X { ... };
Value oriented
class Foo
{
X x_;
public:
X x() const { return x_; }
void x(X x) { x_ = std::move(x); }
}
Here we return a copy of the X member, and accept a copy to overwrite with. Later changes on either side do not propagate. Presumably we only care about the value of x in this case.