dynamic_cast 混淆

10

我放弃了...

$5.2.7/2- "如果T是一个指针类型,v应该是一个指向完整类类型的rvalue指针,并且结果是类型为T的rvalue。如果T是引用类型,则v应该是完整类类型的lvalue,结果是T所引用的类型的lvalue。"

根据上述规定,以下代码应该是良好的形式。

struct A{};
struct B : A{};

int main(){
   B b;
   A a, &ar1 = b;

   B& rb1 = dynamic_cast<B&>(ar1);  // Does not $5.2.7/2 apply here?
   B& rb2 = dynamic_cast<B&>(a);    // and also here?
}

但事实并非如此。所有编译器都会抱怨 dynamic_cast 操作数不符合多态,根据 $5.2.7/6 规定,否则 v 必须是指向多态类型的指针或左值。所以我的问题是 $5.2.7/2 是什么意思?为什么这里会用到 $5.2.7/6 规定?
3个回答

9

所有5.2.7中的要求应该一起遵守。不能仅停留在5.2.7/2上,然后开始编写所谓满足“5.2.7/2及以下”的所有内容的代码。整个5.2.7定义了dynamic_cast的规范。

多态要求是因为它是有条件的而被单独提出来的。当您使用dynamic_cast进行向上转换时,多态要求不适用(实际上,dynamic_cast在向上转换时等同于static_cast)。多态要求仅适用于您使用dynamic_cast进行向下转换或交叉转换时。

dynamic_cast的规范是按顺序组织的,这意味着它首先处理简单情况,然后再处理更复杂的应用程序。您应该逐步阅读它,直到涵盖您的特定情况。沿着这条路径阅读的所有内容都是累积的,“否则”表示:“如果我们尚未涵盖您的情况,请继续阅读”。


2
嗯。好的。但是“otherwise”的意思是什么? - sharptooth
恰好是我的问题。标准中许多其他部分使用“否则”的用户给出了一种“逐步”算法处理的方式。 - Chubsdad
谢谢AndreyT。我认为Steve以一种令人信服的方式表达了它。'否则'适用于5.2.7/5而不是所有先前的条款。我希望这样的混淆有可能被提交给标准委员会进行潜在解决。 - Chubsdad
如果没有对象切片,就没有适当的向上转型方式,那么 dynamic_cast 就无法进行向上转型。 - Krishna Oza

4
为了像你的例子一样进行向下转换,结构体A需要是多态的,并且具有RTTI。这是一个可以工作到某种程度的调整版本:
struct A{virtual void f(){}};
struct B : A{};

int main(){
   B b;
   A a, &ar1 = b;

   B& rb1 = dynamic_cast<B&>(ar1);  // Does not $5.2.7/2 apply here?
   //B& rb2 = dynamic_cast<B&>(a);    // and also here?
}

通过添加虚拟使其多态,启用了该类的RTTI,允许向下转换。
请注意,您的第二个示例是无法工作的——因为您正在将pod(a)强制转换为pod的引用——这是不允许的。
更新:
您的代码在5.2.7.5下是不允许的,并且在5.2.7.6下也不允许。我的调整使它可以在5.2.7.6下运行。

谢谢Alex。不过那不是我的问题。 - Chubsdad

3
在这种情况下,“otherwise”的意思是“除非5.2.7/5中的条件适用”。
你可以看出来,因为/2对程序关于dynamic_cast的操作数提出了要求(注意“v必须是左值”的“shall”语言与“结果是左值”的“is”语言之间的区别)。与标准中的其他地方一样,表达要求并不一定意味着它是唯一的要求。其他条款可以规定额外的要求。在这种情况下,/6规定了一个额外的要求,仅适用于特定情况,取决于T和v的静态类型。
/3、/4、/5告诉你结果的,它们与/2的要求完全一致。它们中没有一个以“否则”开头。所以对我来说,很明显它们不构成从/2开始的“else if”链。
一些括号或其他东西可能会使这更清晰(即/6中的“otherwise”应用于/5中的“if”,而不是/2、/3或/4中的“if”)。但这只是不同的风格。
除此之外,在/5中的“otherwise”在逻辑上无法有意义地适用于/2中的条件。/1说T必须是“指向完整类类型的指针或引用,或cvvoid*”。/2涵盖了两种情况——指针类型和引用类型。这就是所有的。/2没有“otherwise”(除非它说“否则,符合要求的编译器必须发出诊断”,但那是隐含的)。

标准中是否还有其他情况下,“otherwise”被使用得可能不太清晰? - Chubsdad
在标准中,“shall”和“will”的用法有什么区别? - Chubsdad
我不能保证它是100%一致的,但“shall”用于强调某些东西是限制/要求(取决于上下文是针对程序还是实现)。而“will”或“is”则用于陈述你可以依赖的真实情况,而不是你实际所做的事情。当然,大多数这些“is”的事情最终也会成为实现的要求,因此这主要是一个强调的问题。 - Steve Jessop
这是我另一个想法:“如果T是指针类型,v应该是完整类类型的指针rvalue,结果是类型为T的rvalue。”因为它谈到“T = pointer,v = Rvalue”,我认为/6谈到“T = pointer,v = lvalue”。我认为这个想法有一定的价值。 - Chubsdad
@Chubsdad:我明白你的意思,但是文本是“如果T是指针类型,则v应该是rvalue”,而不是“如果T是指针,并且v是rvalue”。这样就没有其他选择了,v只能是完整类类型的rvalue指针。你越读标准,就越习惯处理它的小细节...在这种情况下,还有另一个微妙之处,即lvalues转换为rvalues。因此,实际上v可以是一个表达式,可以被视为lvalue,但/2表示它将被视为rvalue。限制是类型。 - Steve Jessop
@Chubsdad:发现另一个模棱两可的“否则”。4.7/3:“如果目标类型为有符号类型,则该值在目标类型(和位域宽度)可以表示时不变;否则,该值是实现定义的。”单独阅读此内容,您不会知道“否则”是适用于“如果它可以表示”,还是“如果目标类型为有符号类型”。当然,它是前者,因为上一个段落说明了目标类型为无符号类型时会发生什么。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接