static_cast被误用了吗?

15

我对static_cast有复杂的感觉,因为它是最安全的C++转换,但同时允许安全和不安全的转换,所以你必须知道上下文才能确定它是否实际上是安全的或可能导致UB(例如在向子类转换时)。

那么为什么没有更安全的显式转换?这里有一个例子,它可能会很有用。在COM中,他们必须将接口指针作为void** ppv返回,因此需要进行"必须"显式转换。

*ppv = (IInterface*) this;

随后建议使用更安全的 C++ 转换来替代它

*ppv = static_cast<IInterface*>(this);

但是在这里使用static_cast是否有意义呢?this是从IInterface派生的类,因此可以简单地编写:

IInterface* p = this; // implicit conversion to base, safe for sure
*ppv = p;

或者使用类似的辅助工具
template<class T, class U>
T implicit_cast(U p) { return p; }

*ppv = implicit_cast<IInterface*>(this);

那么,static_cast 有时被错误使用,可以(应该?)在某些情况下用这个 implicit_cast 替代,这是真的吗,还是我漏掉了什么?

编辑:我知道 在 COM 中需要转换,但不一定要使用 static_cast,隐式转换就足够了。


1
注意:请参考https://dev59.com/uXNA5IYBdhLWcg3wrf43#869597了解“implicit_cast”的“正确”实现方式(以及一个很好的解释)。 - gx_
1个回答

6
在这种情况下,我认为向上转型是已知的,并且因此使用static_cast应该是完全安全的。
看起来使用implicit_cast可能更安全,并且允许您明确选择要隐式转换为哪个基类(这显然是COM所需的)。
我用g++进行了快速测试,implicit_cast确实按预期为不同的基类返回不同的地址。
但请注意,在您的第一句话方面,我会认为dynamic_cast实际上比static_cast更安全,因为如果无法完成转换,它将返回null或抛出异常。相反,static_cast将返回一个看起来有效的指针,并让您继续进行,直到您的程序在未来某个时间崩溃,与原始错误转换无关。
测试程序:
#include <iostream>

class B1
{
public:
    virtual ~B1() {}
};

class B2
{
public:
    virtual ~B2() {}
};

class Foo : public B1, public B2
{
};

template<class T, class U>
T implicit_cast(U p) { return p; }

int main()
{
    Foo* f = new Foo;
    void **ppv = new void*;

    *ppv = implicit_cast<B1*>(f);
    std::cout << *ppv << std::endl;;
    *ppv = implicit_cast<B2*>(f);
    std::cout << *ppv << std::endl;;

    return 0;
}

确实,dynamic_caststatic_cast 更安全,但我没有提到它,因为我从未见过它被实际使用过(我听说会有性能损失,但不知道有多大)。 - Roman L
3
在Visual C++中,大约需要2000个处理器周期,如果你将COM对象转换为错误的类型,编译器不会捕获到错误- 只有在运行时才会得到一个空指针。 - sharptooth
@sharptooth:没错,在COM环境中,dynamic_cast绝对不是最好的选择。我甚至不确定在哪种情况下它可能是一个好的选项 - 在大多数情况下,它可以被运行时或编译时的多态性替代,这样既更安全也更快。 - Roman L

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