C++ C++;:构造器歧义

C++ C++;:构造器歧义,c++,constructor,ambiguity,ambiguous,C++,Constructor,Ambiguity,Ambiguous,这是否会造成歧义 class A { ... }; class B : public A { //... B(const B& b); B(const A& a); //... }; 运算符和生成引用 在本例中,第一种类型是对Vector3D类型的引用,另一种类型是对Vector2D类型的引用,因此它们是不同的类型 由于它们是不同的类型,它们将使构造函数的签名不同,并且不会产生歧义 答案是否定的。更新:询问者似乎已经从问题中删除了类名。下面是编写此答案的原始代码: class

这是否会造成歧义

class A { ... };
class B : public A {
//...
B(const B& b);
B(const A& a);
//...
};

运算符
生成引用

在本例中,第一种类型是对Vector3D类型的引用,另一种类型是对Vector2D类型的引用,因此它们是不同的类型

由于它们是不同的类型,它们将使构造函数的签名不同,并且不会产生歧义


答案是否定的。

更新:询问者似乎已经从问题中删除了类名。下面是编写此答案的原始代码:

class Vector2D { ... };
class Vector3D : public Vector2D {
//...
Vector3D(const Vector2D& b);
Vector3D(const Vector3D& a);
//...
};
我早些时候说“不”,但我改变了主意。这是不明确的,我想不出一个好的理由让
Vector3D
继承
Vector2D

从数学的角度来看,2D向量空间可以扩展到3D向量空间,但有许多扩展可供选择——它们不是唯一的。因此,当您创建一个从2D向量类继承的3D向量类时,您将2D空间的一个嵌入设置为“特权”,而所有其他嵌入设置为低级。我认为这不是特别有用

你正在做的另一件事是你在说“每个3D向量都是2D向量”,这有点傻。例如,您可能编写了一个函数:

// Compute the angle between two vectors
double angle(Vector2D x, Vector2D y);
// Compute the angle between two 3D vectors
double angle(Vector3D x, Vector3D y);
假设您忘记编写3D向量的版本。现在,编译器将无法向您提供错误消息:相反,您的3D向量将使用您在创建类时选择的“特权”投影投影到2D空间,并且您将得到错误的答案。现在假设您添加了另一个函数:

// Compute the angle between two vectors
double angle(Vector2D x, Vector2D y);
// Compute the angle between two 3D vectors
double angle(Vector3D x, Vector3D y);
现在,还有一个问题

Vector3D x = ...;
Vector2D y = ...;
double a = angle(x, y);
这将使用x的2D投影,同样,您不会得到编译器警告。你只会得到错误的答案

总结:这是最糟糕的模糊性:编译器会在这种模糊性中给出你意想不到的答案


Vector3D不应从Vector2D继承。这在数学上是不合逻辑的。

在引用基和派生类型的重载构造函数之间进行选择时,通常没有歧义

如果参数是从基类型(
a
)派生的类型,但不是派生类型(
B
)或从派生类型派生的类型,则显然只有引用基的构造函数才是匹配的

如果参数是派生类型,则调用获取基类的构造函数所需的转换需要派生到基类的转换,此时调用获取派生类型引用的构造函数只需要标识转换,因此后者更匹配

如果参数是从派生类型(
B
)派生的类,则采用派生类型(
B
)的构造函数仍然是更好的匹配,因为标准规定绑定到派生类型的引用优于绑定到该类型的基的引用。(ISO/IEC 14882-2011 13.3.3.2/4-隐式转换序列排序[超过ics.rank])

显然,如果你足够努力,你仍然可以产生歧义

例如


也许编译器不会。但是3D向量真的是2D向量吗?它不会让你免于做类似于
Vector3D x,y(静态_cast(x))尽管如此,&运算符(的地址)生成指针,而不是引用???@CharlesBailey我不知道您是否在提出问题或陈述,但在本例中,&运算符将生成引用。我在查询您的陈述,可能我不理解您陈述的上下文<代码>&
在引用声明中不是运算符,而是声明符的一部分。类似于
*
,它可以在声明中声明指针,但当用作运算符时,会有效地删除其操作数的“指针性”,从而取消对指针的引用。@Casey:编译器会告诉您是否存在歧义,您只需试着编译代码并测试自己。