2012-07-28 93 views
0

这会不会引起歧义?:C++:构造歧义

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

不,它不会。 – 2012-07-28 21:24:35

+4

也许不在编译器中。但是3D矢量真的是2D矢量吗? – 2012-07-28 21:26:07

+0

它不会拯救你从类似'Vector3D x,y(static_cast (x));'虽然... – 2012-07-28 21:27:26

回答

4

更新:看来,提问者已经从问题擦洗类名。这是写这个答案的原始代码片段:

class Vector2D { ... }; 
class Vector3D : public Vector2D { 
//... 
Vector3D(const Vector2D& b); 
Vector3D(const Vector3D& a); 
//... 
}; 

我之前说过“不”,但我改变了主意。这是模棱两可的,我想不出有一个很好的理由让Vector3D继承自Vector2D

从数学的角度来看,2D矢量空间可以扩展到3D矢量空间,但有很多扩展可供选择 - 它们不是唯一的。因此,当您制作从2D矢量类继承而来的3D矢量类时,您正在将3D空间中的2D空间嵌入到“特权”的3D空间中,并且所有其他嵌入均较差。我认为这不是特别有用。

你正在做的另一件事是你说“每个3D矢量都是2D矢量”,这实在是太愚蠢了。比如,也许你编写一个函数:

// Compute the angle between two vectors 
double angle(Vector2D x, Vector2D y); 

让我们假设你忘记写的版本3D矢量。现在,编译器将无法给您一个错误信息:相反,您的3D矢量将使用您在创建类时选择的“特权”投影投影到2D空间中,并且您将得到错误的答案。现在假设你添加了另一个功能:

// 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。这在数学上不合逻辑。

+1

@Casey:编译器会告诉你是否存在歧义,你需要做的就是编译代码并测试自己。 – 2012-07-28 23:03:28

1

操作&生成引用。

在你的情况下,第一种类型是对Vector3D类型的引用,另一种是对Vector2D类型的引用,因此它们是不同的类型。

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

答案是否定的

+0

>运算符(地址)产生指针,而不是引用 – 2012-07-28 21:55:29

+0

@CharlesBailey我不明白你正在提出一个问题或一个陈述,但在这种情况下,&运算符将生成一个引用。 – user827992 2012-07-28 22:09:43

+0

我在查询你的陈述,也许我不了解你的陈述的上下文。不是操作符,它是声明器的一部分,与声明中可用于声明指针的'*'类似,但作为操作符使用时,可以有效地除去其操作数的“指针性”,取消引用指针 – 2012-07-28 22:15:39

2

在重载构造函数之间进行选择时,它们引用基本类型和派生类型,通常不存在歧义。

如果参数的类型派生自基类型(A),但不是派生类型(B)或衍生自派生类型的派生类型,那么显然只有参考base的构造函数是匹配项。

如果参数是派生类型,那么调用构造函数获取基类所需的转换需要派生类到基类型,此时调用构造函数的参考派生类型只需要一个身份转换,所以后者是更好的匹配。

如果参数是从所述派生类型(B)派生的类的,则构造服用派生类型(B)仍然是一个更好的匹配,因为标准说,结合到派生类型的基准优于绑定到该类型的基准的引用。 (ISO/IEC 14882:2011 13.3.3.2/4 - 隐式转换序列排序[over.ics.rank])

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

E.g.

struct S { 
    operator A() const; 
    operator B() const; 
}; 

S s; 
B b(s);