7

考虑以下:

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 系统必须已经有这样的代码。有什么办法可以访问它并打开它吗?

还是有更好的方法来做我想做的事情?

4

3 回答 3

4

C++11几乎就在那里。

在 C++03 中这是不可能的,因为获得编译时常量(case需要)的唯一方法是通过类型系统。由于typeid总是返回相同的类型,它不能为switch.

C++11 添加constexprtype_info::hash_code作为类型的唯一标识符,但不组合它们。您可以typeid在类型名称或静态类型表达式上的常量表达式中使用,但因为hash_code是非constexpr函数,您不能调用它。

当然,有多种解决方法,您描述了其中一种,其中最通用的一种是使用模板元编程将访问者应用于类型向量。

于 2012-12-24T03:51:21.100 回答
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++ 非常不擅长),这将在编译时失败。

于 2012-12-24T04:00:55.187 回答
0

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 中的枚举,正是因为这个原因,人们倾向于滥用它们作为类型代码。一定要使用虚函数。

嘿,虚拟功能!

当您需要动态分派类型时,它们非常有用。

因此,我建议为此使用虚函数。:)


编辑:模板化::g以避免实际代码中可能出现的歧义。

于 2012-12-24T04:47:26.340 回答