C++11 动态投射 如果其他链 -> 开关
C++11 Dynamic Cast If Else Chain -> Switch
考虑以下内容:
struct B { };
template<typename T>
struct D : B
{
T t;
}
void g(int i) { ... }
void g(string s) { ... }
void g(char c) { ... }
void f(B* b)
{
if (dynamic_cast<D<int>*>(b))
{
g(dynamic_cast<D<int>*>(b)->t);
}
else if (dynamic_cast<D<string>*>(b))
{
g(dynamic_cast<D<string>*>(b)->t);
}
else if (dynamic_cast<D<char>*>(b))
{
g(dynamic_cast<D<char>*>(c)->t)
}
else
throw error;
};
这里只有三种可能的T类型——int, string, char——但如果可能的类型列表较长,例如n,则if else链将花费O(n)来执行。
处理这个问题的一种方法是在D中存储一个额外的类型代码,然后在类型代码上存储switch
。
RTTI系统必须已经有这样的代码。有什么方法可以进入并打开它吗?
或者有更好的方法来做我正在做的事情?
c++ 11几乎是。
在c++ 03中这是不可能的,因为获得编译时常数的唯一方法(case
需要)是通过类型系统。由于typeid
总是返回相同的类型,因此不能为switch
产生不同的替代品。
c++ 11增加了constexpr
和type_info::hash_code
作为类型的唯一标识符,但没有将它们组合起来。可以在类型名或静态类型表达式的常量表达式中使用typeid
,但由于hash_code
是非constexpr
函数,因此不能调用它。
当然,有各种各样的解决方法,其中一种是您所描述的,其中最常用的是使用模板元编程在类型向量上应用访问者。
由于只有少数类型是有效的,因此可以使用虚函数和模板专门化来解决这个问题:
struct B
{
virtual void g() = 0;
}
template<typename T>
struct D : public B
{
T t;
};
template<>
struct D<int> : public B
{
int t;
void g() { /* do something here */ }
};
template<>
struct D<std::string> : public B
{
std::string t;
void g() { /* do something here */ }
};
template<>
struct D<char> : public B
{
char t;
void g() { /* do something here */ }
};
void f(B* b)
{
b->g();
}
如果您提供了错误的类型,或者需要进行运行时检查(这是c++非常不擅长的),这将在编译时失败。
在c++中,运行时切换类型的主要选择是虚函数。
非常简单:
#include <string>
#include <iostream>
using namespace std;
struct Base
{
virtual void g() const = 0;
};
template< class Type > void g( Type const& );
template<> void g( int const& ) { cout << "int" << endl; }
template<> void g( string const& ) { cout << "string" << endl; }
template<> void g( char const& ) { cout << "char" << endl; }
template< class Type >
struct Derived: Base
{
Type t;
virtual void g() const override { ::g<Type>( t ); }
};
void f( Base& b ) { b.g(); }
int main()
{
Derived<int>().g();
}
你可以,它也是有效的,O(1)而不是愚蠢的O(n)。另外,使用静态(编译时)类型检查代替动态(运行时)类型检查,可以节省大量令人讨厌的测试。我还能说什么呢?真的,忘记类型代码和枚举之类的吧。记住Bertrand Meyer在Eiffel中选择不支持枚举,正是因为这个原因,人们倾向于滥用它们作为类型代码。使用虚函数
嘿,虚函数!
当你想在类型上动态分派时,它们真的很有用。
因此,我建议使用虚函数。:)
EDIT:模板化::g
,以避免在实际代码中可能出现的歧义。
- 既然存在危险,为什么项目要使用-I include开关
- 为什么这个音频包络不能通过开关的情况?
- EASTL矢量<向量<int>>连续的
- 有人知道为什么在开关中使用stoi函数会返回恒定的错误吗
- C 和 C++ 中开关语句的案例标签的常量值,但显示不同的行为
- 在 c++ 中在开关情况下使用和不使用"break"时的不同输出
- 为什么我的开关/机箱在使用枚举时默认?
- 为什么布尔开关语句有编译器警告?
- 如何使用"equal to"以外的评估编写开关语句
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 无法找到简单的开关大小写枚举错误
- 未达到的情况会影响开关外壳性能
- C++:我的开关盒循环转到第一种情况
- 有没有办法在C++将字符串与开关语句一起使用?
- 开关:无外壳中断
- 带有开关语句的 do-while 循环 -- 无穷循环错误
- 将编译器开关添加到 Eclipse CDT 内置编译器设置生成?
- C++ 我的开关格式中的循环不允许我显示菜单选项或接受输入?
- 如何在开关的情况下使用右值引用
- 如何在不同的开关大小写语句上使用对象的类成员函数?