C ++ 11 Dynamic Cast If Else Chain — & gt; переключатель

Учтите следующее:

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 уже должна иметь такой код. Есть ли способ получить к нему доступ и включить его?

Или есть лучший способ сделать то, что я пытаюсь сделать?

6

Решение

C ++ 11 — это почти там.

В C ++ 03 это было невозможно, потому что единственный способ получить константу времени компиляции (которая case требуется) прошел через систему типов. поскольку typeid всегда возвращает один и тот же тип, он не может произвести разные альтернативы для switch,

C ++ 11 добавляет constexpr а также type_info::hash_code как уникальный идентификатор типов, но не объединяет их. Ты можешь использовать typeid в константном выражении типа имени или статически типизированных выражений, но потому что hash_code это не constexpr функция, которую вы не можете вызвать

Конечно, есть различные обходные пути, один из которых вы описываете, и самый общий из которых применяет посетителя к вектору типов с помощью шаблонного метапрограммирования.

4

Другие решения

Поскольку допустимо только несколько типов, вы можете решить это с помощью виртуальных функций и специализации шаблонов:

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 ++ довольно плохо).

3

Основным выбором для переключения типа во время выполнения в 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). Кроме того, со статической (во время компиляции) проверкой типов вместо динамической (во время выполнения) проверки типов, что экономит довольно раздражающее количество тестов. Что еще я могу сказать? Действительно, забудьте про код типа и перечисления и тому подобное. Помните, что Бертран Мейер выбрал не поддерживается перечисляет в Eiffel, по этой причине, что люди склонны злоупотреблять ими для кодов типов. Используйте виртуальные функции.

Эй, виртуальные функции!

Они действительно полезны, когда в противном случае вы хотите динамическую отправку по типу.

Поэтому я рекомендую использовать для этого виртуальные функции. 🙂


РЕДАКТИРОВАТЬ: шаблонизированный ::g во избежание возможных неясностей в реальном коде.

-1
По вопросам рекламы [email protected]