在C ++中重载函数时,是否存在涉及模糊类型的以下问题的优雅解决方案?我想要两种不同的类型:“距离”和“角度”,它们在语义上意味着不同的东西,但实际上可能具有相同的类型。当尝试在C ++中执行此操作时,我遇到了函数重载的问题:
typedef float distance;
typedef float angle;
class Velocity {
public:
Velocity(distance dx, distance dy) { /* impl */ }
Velocity(angle theta, distance magnitude) { /* impl */ }
};
当我尝试编译它时,我得到“构造函数不能重新声明”。但是(由于某种原因,我无法弄清楚)当我在我的实际应用程序代码库中执行此操作时,类编译但后来我得到“调用'Velocity'的构造函数是不明确的”当我做这样的事情时:
distance dx;
distance dy;
Velocity v(dx, dy);
这个问题有没有优雅的解决方案?一个不令人满意的解决方案是仅更改其中一个数量的类型
typedef double distance;
但由于只有少数不同的浮点类型,因此显然无法扩展。我尝试过的另一个选择是使用模板
template <typename distance, typename angle>
class Velocity {
public:
Velocity(distance dx, distance dy) : dx(dx), dy(dy) { }
Velocity(angle theta, distance magnitude) { }
};
但后来我得到“'Velocity'的多次重载实例化到相同的签名'void(float,float)'”如果我用相同的类型实例化它们。即使这样做仍然有点令人不满意,因为我必须在许多地方为Velocity
类型添加模板参数。
typedef
不会创建新类型,它只是为具有不同名称的类型创建别名,并且可能在不同的名称空间中创建,类似于using
。
因此,你的构造函数都是有效的float, float
。
如果要创建实际的新类型,可以创建包含该类型的新结构或类。 C ++ chrono类型,如std::chrono::seconds
包装整数。这样做也允许更具体的重载,例如你可能会说displacement = velocity * time
,尽管这样做很快就会需要很多类型和运算符重载。
当你为float
和double
重载时也要小心,我肯定永远不会这样做,因为它们有不同的含义。考虑像0
和1
这样的文字会发生什么,或者像int
这样的类型的隐式转换。
您可以围绕距离和角度创建一个非常轻量级的包装,然后使用文字!
struct Distance {
double value;
};
struct Angle {
double value;
};
class Velocity {
public:
Velocity(Distance dx, Distance dy) { /* impl */ }
Velocity(Angle theta, Distance magnitude) { /* impl */ }
};
然后,您可以在构造Velocity
时包装值:
// Create by distance
Velocity v1(Distance{5.0}, Distance{10.0});
// Create by angle
Velocity v2(Angle{1.5}, Distance{1.0});
我们还可以提供用户定义的文字,以便您可以像这样编写v1
和v2
:
Velocity v1(5.0_meters, 10.0_meters);
Velocity v2(60.0_degrees, 10.0_meters);
编写用户定义的文字非常简单:
Distance operator ""_meters(double value) {
return Distance{value};
}
Angle operator ""_degrees(double value) {
return Angle{value / 180.0 * PI};
}
是的,您需要不同类型才能拥有重载。
一种解决方案是通过更改函数名称来避免重载。
另一种是通过创建包裹浮动的class
或struct
来创建自定义类型。
然后,您可以重载某些运算符以实现所需的行为,但在我看来,最好更改函数的名称