C++中的static_cast和引用

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

int main()
{
    A a;
    A& a_ref = a;

    static_cast<B>(a); // *1
    static_cast<B&>(a_ref); // *2

    return 0;
}

(*1)会产生一个错误,我了解原因。(*2)能够编译通过,但是为什么呢?假设B包含一些属性,如果我将a_ref强制转换为B&然后尝试访问这些属性会怎样呢?我想我会遇到运行时错误或者其他问题。

所以,我发现存在这样一种情况可能导致程序崩溃,而且没有办法可以避免,不像使用dynamic_cast,我们可以检查转换结果是否为空或者在try-catch区域放入代码。那么当我需要进行引用类型的强制转换并确保得到正确的引用时,我该如何应对这种情况呢?


你可以使用 static_cast 将 lvalue 下转型为派生类的引用,因为没有隐式转换方式(与从派生类到基类的转换相反)。只有在事先知道它是安全的情况下(即您要转换的 lvalue 是派生类型或派生类型的情况),才应仅用于此目的。 - dyp
@JohnDibling:我需要它来将A&强制转换为B&并处理属性。 - fogbit
编译器只能发现某些类型的问题。在孤立的情况下,第*2行似乎没问题,因为在某些情况下将A&强制转换为B&是有效的。编译器必须进行更复杂的分析才能确定在这种情况下它实际上是无效的。这可能要么太困难,要么太慢而不切实际。 - Vaughn Cato
@fogbit:我理解你的意思。你确定你的设计没有问题吗? - John Dibling
@John Diblin:只要我们不生活在独角兽的世界中,而我正在处理遗留代码,当然设计是有问题的 : -) - fogbit
显示剩余2条评论
4个回答

10
从标准草案n3337第5.2.9/2节:
如果存在从“指向D”的指针到“指向B”的指针的有效标准转换(4.10),cv2是与cv1相同或更大的cv限定符,且B既不是D的虚拟基类,也不是D的虚拟基类的基类,则可以将类型为“cv1 B”的lvalue强制转换为类型为“reference to cv2 D”的引用,其中B是类类型,D是从B派生(第10条)的类。
在您的情况中: B是从A派生的类,两者均为非const,并且允许从A*B*的转换,A不是D的虚拟基类。

6
static_cast<>仅检查类型是否兼容。
在情况1中,类型不直接兼容,因为没有运算符描述A和B之间的复制关系。
在情况2中,转换是引用转换,就编译器而言,A*可以强制转换为B*,因为它们是兼容的。编译器将不知道指针a_ref持有什么,这就是它允许你使用它的原因。dynamic_cast<>还检查指针指向的类。

5

这是我使用 boost::polymorphic_downcast文档)的原因之一 - 在调试中,它使用 dynamic_cast 并跟随一个 assert,在发布中它是 static_cast,所以没有性能损失。


谢谢提供的信息,我会查看“boost::polymorphic_downcast”。 - fogbit

2

(*2) 编译通过,但为什么?

一般情况下,你无法在静态环境中检查动态类型;而 static_cast 不会进行任何动态类型检查。它允许进行任何可能根据静态类型是有效的转换,包括那些根据动态类型无效的转换。

如果我将 a_ref 转换为 B&,然后尝试访问属性会怎样?

未定义行为。如果使用 static_cast,则你需要确保转换是有效的。

当我需要转换引用并确保我得到正确的引用时,我该如何处理这种情况?

对于多态类型,请使用 dynamic_cast。应用于引用时,如果转换无效,它将抛出 std::bad_cast 异常。

对于非多态类型,你需要自己处理。


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