任何人都可以给我一个真实世界的例子,说明当需要dynamic_cast并且根本无法解决时?我能想到的例子通常可以解决双重调度问题.
如果约束太强,那么一般来说,dynamic_cast的示例也很好.
我希望看到真实的例子,而不是"它通常用于在类型树之间向上和向下转换".
我发现自己处于一种我知道什么类型的东西的情况.Type是三个(或更多)继承级别之一.我调用返回的工厂B*但是T是一个类型的最高级别(如果我的代码知道它是什么)或第二级别.
无论如何,我static_cast在模板中做了一个错误的事情.我的问题是我什么时候可以安全地静态施放?有没有这样的时间?我在这种情况下做到了,因为当我意外地将T作为古怪的东西(已经发生并且)动态转换忽略(并返回null)时,我宁愿得到编译错误.但是,当我知道正确的类型时,指针未被调整,导致我有一个错误的指针.我不确定为什么在这种情况下允许静态转换.
我什么时候可以安全地使用static_cast进行向下铸造?有没有情况?现在似乎总是错误地使用a static_cast(当目的是向下投射时)
好的,我想出了如何重现它.
#include <iostream>
struct B { virtual void f1(){} };
struct D1 : B {int a;};
struct D2 : B {int a, b; };
struct DD : D1, D2 {};
int main(){
void* cptr = new DD(); //i pass it through a C interface :(
B* a = (B*)cptr;
D2* b = static_cast<D2*>(a); //incorrect ptr
D2* c = dynamic_cast<D2*>(a); //correct ptr
std::cout << a << " " <<b << " " …Run Code Online (Sandbox Code Playgroud) 最近我查看了一个开源项目的代码,我看到了一堆表单的声明T & object = *dynamic_cast<T*>(ptr);.
(实际上这是在宏中发生的,用于声明遵循类似模式的许多函数.)
对我而言,这看起来像是一种代码味道.我的理由是,如果你知道演员阵容会成功,那么为什么不使用static_cast?如果你不确定,那么你不应该使用断言进行测试吗?由于编译器可以假设任何指针*都不是null.
我问过一位关于irc的开发者,他说,他认为static_cast沮丧是不安全的.他们可以添加一个断言,但即使他们没有,他说你仍然会得到一个空指针取消引用并在obj实际使用时崩溃.(因为,在失败时,dynamic_cast会将指针转换为null,然后当您访问任何成员时,您将从某个非常接近零的值的地址读取,操作系统将不允许这样做.)如果使用a static_cast,和它变坏了,你可能会得到一些内存损坏.因此,通过使用该*dynamic_cast选项,您可以在速度上进行折衷,以获得更好的可调试性.你没有为断言付费,相反,你基本上依靠操作系统来捕获nullptr dereference,至少这是我所理解的.
我当时接受了那个解释,但它让我感到困扰,我又想了一些.
这是我的推理.
如果我理解标准权利,static_cast指针转换基本上意味着做一些固定的指针算术.也就是说,如果我有A * a,我静将它转换为一个相关的类型B *,什么编译器实际上正在与做的就是添加一些偏移指针,偏移量只取决于类型的布局A,B(和其可能的C++实现).void *在静态演员之前和之后,可以通过静态铸造指针和输出它们来测试该理论.我希望如果你看一下生成的程序集,static_cast将会变成"向指针对应的寄存器添加一些固定常量".
一个dynamic_cast指针强制手段,首先检查RTTI和只做静态浇铸如果基于动态类型是有效的.如果不是,那就回来吧nullptr.于是,我想到的是,编译器会在某个点扩大为一个函数dynamic_cast<B*>(ptr),其中ptr的类型是A*到像表达
(__validate_dynamic_cast_A_to_B(ptr) ? static_cast<B*>(ptr) : nullptr)
Run Code Online (Sandbox Code Playgroud)
但是,如果我们再*在dynamic_cast的结果,*的nullptr是UB,因此我们看好隐含的nullptr分支从未发生过.并且合规编译器被允许从中"向后推"并消除空检查,这是克里斯拉特纳着名的博客文章中的一个点驱动的家.
如果测试函数__validate_dynamic_cast_A_to_B(ptr)对优化器是不透明的,即它可能有副作用,那么优化器就无法摆脱它,即使它"知道"nullptr分支没有发生.但是,这个函数可能对优化器不透明 - 可能它对其可能的副作用有很好的理解. …
考虑这个类层次结构:
struct Animal { virtual ~Animal(); };
struct Cat : virtual Animal {};
struct Dog final : virtual Animal {};
Run Code Online (Sandbox Code Playgroud)
我的理解是,将final在class Dog确保没有人能够创建一个类继承Dog,其中,推论,意味着没有人能够创建IS-A类Dog和IS-A Cat同时进行.
考虑这两个dynamic_cast:
Dog *to_final(Cat *c) {
return dynamic_cast<Dog*>(c);
}
Cat *from_final(Dog *d) {
return dynamic_cast<Cat*>(d);
}
Run Code Online (Sandbox Code Playgroud)
GCC,ICC和MSVC忽略final限定符并生成调用__dynamic_cast; 这是不幸的,但并不令人惊讶.
让我感到惊讶的是,Clang和Zapcc都为("总是返回nullptr")生成了最佳代码from_final,但是生成了对__dynamic_castfor 的调用to_final.
这真的是一个错过的优化机会(在一个编译器中显然有人投入一些努力来尊重final演员阵容中的限定符),或者在这种情况下,由于某些微妙的原因,我仍然没有看到优化是不可能的?
给定此类具有隐式转换运算符:
public class MyDateTime
{
public static implicit operator MyDateTime(System.Int64 encoded)
{
return new MyDateTime(encoded);
}
public MyDateTime(System.Int64 encoded)
{
_encoded = encoded;
}
System.Int64 _encoded;
}
Run Code Online (Sandbox Code Playgroud)
我现在可以做以下事情:
long a = 5;
MyDateTime b = a;
Run Code Online (Sandbox Code Playgroud)
但不是以下内容:
long f = 5;
object g = f;
MyDateTime h = g;
Run Code Online (Sandbox Code Playgroud)
这给出了编译时间:
无法将类型'object'隐式转换为'MyDateTime'.
我感觉合理.
现在我修改前面的例子如下:
long f = 5;
object g = f;
MyDateTime h = (MyDateTime)g;
Run Code Online (Sandbox Code Playgroud)
编译好了.现在我得到一个运行时InvalidCastException:
无法将"System.Int64"类型的对象强制转换为"MyDateTime"类型.
这告诉我C#隐式转换运算符仅在编译时应用,并且在.NET运行时试图将对象动态转换为另一种类型时不应用.
我的问题:
顺便说一句,完整的应用程序是我Delegate.DynamicInvoke()用来调用一个带MyDateTime参数的函数,而我传递给的参数的类型很DynamicInvoke长.
c# dynamic-cast type-conversion implicit-cast dynamic-invoke
是的,我知道dynamic_cast如果Base不是多态的话,使用downcast 无法编译,但我的问题与此无关.
class Base {
public:
virtual void bar()
{
cout << "bar\n";
}
};
class Derived: public Base {
public:
void foo()
{
cout << "foo\n";
}
};
int main()
{
Base *pb;
Derived *pd;
pb = new Derived; //Base* points to a Derived object
pd = dynamic_cast<Derived*>(pb);
pd->foo(); //outputs foo
pb = new Base; //Base* points to a Base object
pd = dynamic_cast<Derived*>(pb);
pd->foo(); //outputs foo, too. Why?
}
Run Code Online (Sandbox Code Playgroud)
我以为pb = new Derived; …
我正在使用过时的Visual Studio 2008(让我省去麻烦"这就是你的问题".)这似乎是Visual Studio的一个问题:http://rextester.com/XKFR77690这似乎是一个问题assert宏:http://ideone.com/bhxMi0
鉴于这些结构:
struct base { virtual ~base() {} };
template <typename T>
struct Foo : base { T foo; };
Run Code Online (Sandbox Code Playgroud)
我可以做这个:
base* test = new Foo<pair<int, int>>;
if(dynamic_cast<Foo<pair<int, int>>*>(test) != NULL) cout << "hello world\n";
Run Code Online (Sandbox Code Playgroud)
但是当我在if-statement中使用完全相同的代码时assert:assert(dynamic_cast<Foo<pair<int, int>>*>(test) != NULL)我得到一个错误:
警告C4002:宏
assert
错误C2143的实际参数太多:语法错误:缺少','之前')'
顺便说一句,我可以通过使用C风格的演员来解决这个问题:assert((Foo<pair<int, int>>*)(test) != NULL)但我认为C风格演员阵容static_cast不是dynamic_cast我不想要的.
我放弃了......
$ 5.2.7/2-"如果T是指针类型,则v应该是指向完成类类型的指针的右值,结果是类型为T的rvalue.如果T是引用类型,则v应为左值一个完整的类类型,结果是T引用的类型的左值."
根据以上所述,以下代码应该是格式良好的.
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?
}
Run Code Online (Sandbox Code Playgroud)
但事实并非如此.所有编译器都抱怨dynamic_cast的操作数不符合多态
$ 5.2.7/6-否则,v应该是多态类型的指针或左值(10.3).
所以我的问题是$ 5.2.7/2是什么意思?为什么$ 5.2.7/6会在这里踢?
在下面的代码中,在构造obj案例1时,我们也构造了一个derived类对象,但它的成员函数是不可访问的obj.因此,在向下转换(即,在情况2中),使用obj作为源时,我们已经在其中构造derived了它.为什么obj需要多态?
如果我把我与上面的描述混淆了,为什么obj在向上转换时不需要多态,但是在向下转换时它确实需要在使用时具有多态性dynamic_cast?
class base
{
public:
base()
{
cout<< " \n base constructor \n";
}
};
class derived : public base
{
public:
derived()
{
cout << " \n derived constructor \n";
}
};
base *obj = dynamic_cast<base*> (new derived); // case 1: explicitly upcasting
derived *OBJ = dynamic_cast<derived*> (obj); // case 2: error
Run Code Online (Sandbox Code Playgroud) 我今天偶然发现了一个问题,似乎无法解决.我正在编译一个共享库,其中包含一个模板化的类(Derived<T>,其基础是Base)以及该类的一些显式实例化.我希望库用户从这个模板化的类扩展.当我尝试将出现问题dynamic_cast的用户的情况下,从Base*到Derived<T>*.
我已将问题缩小到这个MWE:
共享库包含以下文件:
Base.h
#ifndef BASE_H_
#define BASE_H_
class Base {
public:
Base();
virtual ~Base();
};
#endif /* BASE_H_ */
Run Code Online (Sandbox Code Playgroud)
Derived.h
#ifndef DERIVED_H_
#define DERIVED_H_
#include <Base.h>
template <typename T>
class Derived : public Base {
public:
Derived();
virtual ~Derived();
};
#endif /* DERIVED_H_ */
Run Code Online (Sandbox Code Playgroud)
Derived.cpp
#include <Derived.h>
template <typename T>
Derived<T>::Derived() :
Base() {
}
template <typename T>
Derived<T>::~Derived() {
}
// explicit instantiations
template class Derived<float>;
template class …Run Code Online (Sandbox Code Playgroud) dynamic-cast ×10
c++ ×9
assert ×1
c# ×1
c++11 ×1
casting ×1
downcast ×1
final ×1
gcc ×1
polymorphism ×1
static-cast ×1
templates ×1