如何模拟 C# 的 {get; 在 C++ 中设置;}?

Wer*_*Elf 3 c++

我正在尝试 lambda 函数,并设法在 C++ 中重新创建“获取”功能。我可以在不使用括号的情况下获取函数的返回值。这是一个示例类,我在其中实现:

using namespace std;

struct Vector2 {
    float x;
    float y;
    float length = [&]()-> float {return sqrt(x * x + y * y); }();
    float angle = [&]()-> float {return atan2(y, x); }();

    Vector2() : x(0), y(0) {}
    Vector2(float a, float b) : x(a), y(b) {}
    ~Vector2() {}
    Vector2(Vector2& other) : x(other.x), y(other.y) {}
    Vector2(Vector2&& other) = delete;
    void operator =(Vector2&& other) noexcept{
        x = other.x;
        y = other.y;
    }
};

int main()
{
    Vector2 vec = Vector2(10, 17);
    printf("%f\n%f\n%f\n%f\n", vec.x, vec.y, vec.length, vec.angle);
}
Run Code Online (Sandbox Code Playgroud)

不过,我目前正在尝试重新创建 C# 具有的“设置”功能。但我失败了。我尝试添加这个:

void angle = [&](float a)->void {
    float l = length;
    x = cos(a) * l;
    y = sin(a) * l;
};
Run Code Online (Sandbox Code Playgroud)

但收到“不允许不完整的类型”错误。我不确定它是否应该是这样的,即使我没有收到错误。是否有可能在 C++ 中重新创建 C# 的“设置”功能?

我知道我可以只使用一个方法SetAngle(float a){...},但这不是重点。

Mil*_*nek 5

TL;DR:不要

你所拥有的不是一个 getter,它只是一个普通的数据成员,在对象初始化时计算一次。

一般来说,C++ 不支持 C# 样式的属性。通常的 C++ 风格的解决方案是仅使用一对成员函数(如果需要单独保存值,可能还使用一个数据成员),即

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;
    }

    // ...
};
Run Code Online (Sandbox Code Playgroud)

您可以获得一些接近 C# 风格的属性,但您总是会遇到它们不能完美工作的边缘情况。例如,以下内容在许多情况下都适用:

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";
}
Run Code Online (Sandbox Code Playgroud)

但这在边缘情况下仍然会崩溃,特别是当您将其与模板和/或auto类型推导混合时。例如:

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';
Run Code Online (Sandbox Code Playgroud)

另请注意,这里有一个错误。考虑一下:

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";
}
Run Code Online (Sandbox Code Playgroud)

您可以通过设置不可复制来解决这些问题Property,但随后您会强制使用它的任何类来实现自定义复制构造函数。另外,虽然这会使auto情况变得“安全”,但它是通过将其转变为编译错误来实现的。还是不理想。