我正在尝试 lambda 函数,并设法在 C++ 中重新创建“get”功能。我可以在不使用括号的情况下获取函数的返回值。这是一个示例类,我在其中实现了这个:
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);
}
但是,我目前正在尝试重新创建 C# 具有的“设置”功能。但我失败了。我尝试添加这个:
void angle = [&](float a)->void {
float l = length;
x = cos(a) * l;
y = sin(a) * l;
};
但是收到“不允许不完整的类型”错误。我不确定它是否应该是这样的,即使我没有收到错误。是否有可能在 C++ 中重新创建 C# 的“设置”功能?
我知道我可以使用一种方法
SetAngle(float a){...}
,但这不是重点。
TL;博士:不要
你拥有的不是一个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;
}
// ...
};
您可以获得接近 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";
}
但这在边缘情况下仍然会崩溃,特别是当你将它与模板和/或
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';
另请注意,这里有一个错误。考虑一下:
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";
}
您可以通过使
Property
不可复制来解决这些问题,但随后您会强制使用它的任何类实现自定义复制构造函数。另外,虽然这会使 auto
情况变得“安全”,但它是通过将其转变为编译错误来实现的。还是不太理想。
我同意迈尔斯的观点。这不是最好的想法,因为这对于 C++ 开发人员来说是不自然的,并且您应该编写首先且最重要的是易于阅读的代码。
但是,作为一项工程挑战,这里有一个可能的实现:
#include <math.h>
#include <iostream>
template <typename T>
class Member
{
public:
operator T() const { return _value; }
void operator =(const T& value) const { _value = value; } void operator =(T&& value) { _value = std::move(value); }
private:
T _value;
};
class Angle
{
public:
Angle(const Member<float>& x, const Member<float>& y) :
_x(x), _y(y) {}
operator float() const { return atan2(_y, _x); }
private:
const Member<float>& _x, _y;
};
class Obj
{
public:
Member<float> x, y;
Angle angle;
Obj() : angle(this->x, this->y) {}
};
int main()
{
Obj o;
o.x = 3;
o.y = 5;
std::cout << o.x << ", " << o.y << " -> " << o.angle << std::endl;
}
虽然其他解决方案似乎也是可能的,但这个似乎是最优雅的:P
using namespace std;
struct Vector2 {
float x;
float y;
float init_length = [&]()-> float {return sqrt(x * x + y * y); }();
float init_angle = [&]()-> float {return atan2(y, x); }();
__declspec(property(get = GetAngle, put = SetAngle)) float angle;
__declspec(property(get = GetLength, put = SetLength)) float length;
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) = delete;
void Display() {
printf("%f\n%f\n%f\n%f\n\n", x, y, length, angle);
}
float GetLength() {
return sqrt(x * x + y * y);
}
float GetAngle() {
return atan2(y, x);
}
void SetLength(float l) {
float a = GetAngle();
x = cos(a) * l;
y = sin(a) * l;
}
void SetAngle(float a) {
float l = GetLength();
x = cos(a) * l;
y = sin(a) * l;
}
};
int main()
{
Vector2 vec = Vector2(10, 17);
vec.Display();
vec.length = 5;
vec.Display();
}
您可以使用
const
数据成员模拟 const&
get 函数:
class A {
string data;
public:
A( const string& iData ) : data( iData ) { }
// Mimics a const property
const string& Data = data;
};
用途:
A a( "Test" );
puts( a.Data.c_str() );