C ++ специализация, type_of или просто typeid - PullRequest
2 голосов
/ 07 октября 2009

Я хотел бы знать, что лучше использовать в моей ситуации и почему. Прежде всего я слышал, что использование RTTI (typeid) плохо. Кто-нибудь может объяснить, почему? Если я точно знаю типы, что неправильно сравнивать их во время выполнения? Кроме того, есть ли пример, как использовать boost :: type_of? Я не нашел ни одного поиска в могущественном Google :) Другое решение для меня - это специализация, но я бы не хотел специализировать как минимум 9 типов новых методов. Вот пример того, что мне нужно:

У меня есть этот класс

  template<typename A, typename B, typename C>
  class CFoo
  {
     void foo()
     {
       // Some chunk of code depends on old A type
     }

  }

Так что мне нужно скорее проверить typeid (то, что я слышал, ПЛОХО) и сделать эти 3 реализации в примере, например:

 void foo()
   {
      if (typeid(A) == typeid(CSomeClass)
       // Do this chunk of code related to A type
      else
      if (typeid(B) == typeid(CSomeClass)
       // Do this chunk of code related to B type
      else
      if (typeid(C) == typeid(CSomeClass)
       // Do this chunk of code related to C type
   }

Так что же является лучшим решением? Я не хочу специализироваться для всех A, B, C, потому что у каждого типа есть 3 специализации, поэтому я получу 9 методов или только эту проверку typeid.

Ответы [ 5 ]

6 голосов
/ 07 октября 2009

Это плохо, потому что

  1. A, B и C известны во время компиляции, но вы используете механизм времени выполнения. Если вы вызываете typeid, компилятор обязательно включит метаданные в объектные файлы.
  2. Если вы замените «Делать этот фрагмент кода, относящийся к типу A» реальным кодом, использующим интерфейс CSomeClass, вы увидите, что вы не сможете скомпилировать код в случае, когда A! = CSomeClass и A имеют несовместимый интерфейс. Компилятор все еще пытается перевести код, даже если он никогда не запускается. (см. пример ниже)

Обычно вы разбиваете код на отдельные шаблоны функций или статические функции-члены классов, которые могут быть специализированными.

Bad:

template<typename T>
void foo(T x) {
    if (typeid(T)==typeid(int*)) {
        *x = 23; // instantiation error: an int can't be dereferenced
    } else {
        cout << "haha\n";
    }
}
int main() {
    foo(42); // T=int --> instantiation error
}

Лучше:

template<typename T>
void foo(T x) {
    cout << "haha\n";
}
void foo(int* x) {
    *x = 23;
}
int main() {
    foo(42); // fine, invokes foo<int>(int)
}

Приветствия, с

4 голосов
/ 07 октября 2009

Ну, как правило, решения могут быть найдены без RTTI. Это «может» показать, что вы не продумали дизайн программного обеспечения должным образом. Это плохо. Иногда RTTI может быть хорошей вещью.

Тем не менее, есть нечто странное в том, что вы хотите сделать. Не могли бы вы создать промежуточный шаблон, сконструированный примерно так:

template< class T > class TypeWrapper
{
  T t;
public:
  void DoSomething()
  {
  }
};

затем частично специализируемся на функциях, которые вы хотите выполнить следующим образом:

template<> class TypeWrapper< CSomeClass >
{
  CSomeClass c;
public:
  void DoSomething()
  {
     c.DoThatThing();
  }
};

Тогда в вашем классе, определенном выше, вы будете делать что-то вроде ...

шаблон

  class CFoo
  {
     TypeWrapper< A > a;
     TypeWrapper< B > b;
     TypeWrapper< C > c;
     void foo()
     {
       a.DoSomething();
       b.DoSomething();
       c.DoSomething();
     }

  }

Таким образом, он действительно что-то делает в вызове «DoSomething» только если он проходит через частично специализированный шаблон.

2 голосов
/ 07 октября 2009

Боюсь, это не сработает. Эти «куски кода» должны быть компилируемыми, даже если тип не CSomeClass.

Не думаю, что type_of тоже поможет (если он совпадает с auto и decltype в C ++ 0x).

Я думаю, вы могли бы выделить эти три блока в отдельные функции и перегрузить каждый для CSomeClass. (Изменить: о, есть else if's. Тогда вам действительно может понадобиться много перегрузок / специализации. Для чего этот код?)

Edit2: кажется, что ваш код надеется сделать эквивалент следующего, где int - это специальный тип:

#include <iostream>

template <class T>
bool one() {return false; }

template <>
bool one<int>() { std::cout << "one\n"; return true; }

template <class T>
bool two() {return false; }

template <>
bool two<int>() { std::cout << "two\n"; return true; }

template <class T>
bool three() {return false; }

template <>
bool three<int>() { std::cout << "three\n"; return true; }

template <class A, class B, class C>
struct X
{
    void foo()
    {
        one<A>() || two<B>() || three<C>();
    }
};

int main()
{
    X<int, double, int>().foo(); //one
    X<double, int, int>().foo();  //two
    X<double, double, double>().foo(); //...
    X<double, double, int>().foo(); //three
}
2 голосов
/ 07 октября 2009

Проблема заключается в кусках кода, которые вы пишете для каждой специализации.

Неважно, если вы напишите (по длине)

void foo()
{
   if (typeid(A) == typeid(CSomeClass)
    // Do this chunk of code related to A type
   else
   if (typeid(B) == typeid(CSomeClass)
    // Do this chunk of code related to B type
   else
   if (typeid(C) == typeid(CSomeClass)
    // Do this chunk of code related to C type
}

или

void foo()
{
   A x;
   foo_( x );
   B y;
   foo_( y );
   C z;
   foo_( z );
}
void foo_( CSomeClass1& ) {}
void foo_( CSomeClass2& ) {}
void foo_( CSomeClass3& ) {}

Положительным моментом второго случая является то, что когда вы добавляете класс D, компилятор напоминает вам, что существует перегрузка для foo_ отсутствует, которую вы должны написать. Это можно забыть в первом варианте.

1 голос
/ 07 октября 2009

Я думаю, что вы где-то неправильно поняли абстракции.

Я бы попробовал переопределить A, B & C с точки зрения интерфейсов, которые они должны представлять (абстрактные базовые классы в C ++ с помощью чисто виртуальных методов).

Шаблонирование позволяет в основном печатать на уток, но, похоже, CFoo слишком много знает о классах A B & C.

typeid плох, потому что:

  1. typeid может быть дорогим, раздувается двоичные файлы, переносит дополнительные информация, которая не должна быть требуется.
  2. Не все компиляторы поддерживают это
  3. Это в основном нарушает иерархию классов.

Я бы порекомендовал рефакторинг: удалите шаблоны, вместо этого определите интерфейсы для A, B и C и заставьте CFoo использовать эти интерфейсы. Это заставит вас реорганизовать поведение так, чтобы A, B и C были на самом деле связными типами.

...