请考虑以下事项:
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系统必须已经有这样的代码。有没有办法访问它并打开它?
或者有更好的方法来做我想做的事情吗?
答案 0 :(得分:4)
C ++ 11 几乎那里。
在C ++ 03中,这是不可能的,因为获得编译时常量(case
需要)的唯一方法是通过类型系统。由于typeid
始终返回相同的类型,因此无法为switch
生成不同的替代方案。
C ++ 11添加constexpr
和type_info::hash_code
作为类型的唯一标识符,但不会将它们组合在一起。您可以在类型名称或静态类型表达式的常量表达式中使用typeid
,但由于hash_code
是非constexpr
函数,因此无法调用它。
当然,有各种解决方法,其中一个是您描述的,其中最常用的是使用模板元编程将访问者应用于类型向量。
答案 1 :(得分:3)
由于只有少数几种类型有效,您可以使用虚函数和模板特化来解决此问题:
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 ++非常糟糕),这将在编译时失败。
答案 2 :(得分:-1)
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选择不支持在埃菲尔的枚举,因为这个原因,人们倾向于滥用它们来输入类型代码。使用虚函数。
嘿,虚拟功能!
除非您想要在类型上进行动态调度,否则它们非常有用。
所以,我建议使用虚函数。 :)
编辑:模板化::g
,以避免实际代码中出现歧义。