How do I simulate C#’s {get; set;} in C++?

TL;DR: Don’t

What you have isn’t a getter, it’s just a normal data member that’s calculated once when the object is initialized.

In general, C++ doesn’t support C#-style properties. The usual C++-style solution is to just use a pair of member functions (and maybe a data member, if you need to save the value separately), i.e.

struct Vector2 {
    // ...

    float length() const { return sqrt(x * x + y * y); }
    void length(float l) {
        float angle = angle();
        float new_x = l * cos(angle);
        float new_y = l * sin(angle);
        x = new_x;
        y = new_y;
    }

    // ...
};

You can get something close to a C#-style property, but you’ll always run into edge-cases where they don’t work perfectly. For example, here’s something that will work in many cases:

template <typename T>
class Property
{
private:
    std::function<T()> getter_;
    std::function<void(const T&)> setter_;

public:
    Property(std::function<T()> getter, std::function<void(const T&)> setter)
        : getter_{getter},
          setter_{setter}
    {}

    operator T()
    {
        return getter_();
    }

    const T& operator=(const T& val)
    {
        setter_(val);
        return val;
    }
};

struct Vector2
{
    float x;
    float y;
    Property<float> length{
        [this]() { return sqrt(x * x + y * y); },
        [this](float l) {
            float new_x = l * cos(angle);
            float new_y = l * sin(angle);
            x = new_x;
            y = new_y;
        }
    }

    Property<float> angle{
        [this]() { return atan2(y, x); },
        [this](float a) {
            float l = length;
            x = cos(a) * l;
            y = sin(a) * l;
        }
    }

    // ...
};

int main() {
    Vector2 v;
    v.x = 1;
    v.y = 1;

    v.angle = std::numbers::pi / 2;
    std::cout << "(" << v.x << ", " << v.y << ")\n";
}

But this still falls apart in the edge cases, especially when you mix it with templates and/or auto type-deduction. For instance:

Vector2 v;
v.x = 1;
v.y = 1;

auto old_angle = v.angle;
v.angle = std::numbers::pi / 2;

// oops, this prints pi/2, not pi/4 like you probably expected
// because old_angle isn't a float, it's a Property<float> that
// references v
std::cout << old_angle << '\n';

Note also that there’s a bug here. Consider this:

int main() {
    Vector2 v1;
    v1.x = 1;
    v1.y = 1;

    Vector2 v2 = v1;
    v2.angle = std::numbers::pi / 2;

    // Oops, assigning to v2.angle modified v1
    std::cout << "(" << v1.x << ", " << v1.y << ")\n";
}

You could work around these issues by making Property non-copyable, but then you force any class that uses it to implement a custom copy-constructor. Also, while that would make the auto case “safe”, it does so by turning it into a compile error. Still not ideal.