Вопрос

У нас есть подпроект 'commonUtils', который содержит множество общих фрагментов кода, используемых в родительском проекте.Одним из таких интересных материалов, которые я видел, было :-

/*********************************************************************
If T is polymorphic, the compiler is required to evaluate the typeid 
stuff at runtime, and answer will be true.  If T is non-polymorphic, 
the compiler is required to evaluate the typeid stuff at compile time, 
whence answer will remain false
*********************************************************************/
template <class T> 
bool isPolymorphic() { 
   bool answer=false; 
   typeid(answer=true,T()); 
   return answer; 
}

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

class PolyBase {
public:
   virtual ~PolyBase(){}
};

class NPolyBase {
public:
   ~NPolyBase(){}
};


if (isPolymorphic<PolyBase>())
  std::cout<<"PolyBase = Polymorphic\n";
if (isPolymorphic<NPolyBase>())
  std::cout<<"NPolyBase = Also Polymorphic\n";

Но ни одно из них никогда не возвращает true.MSVC 2005 не выдает предупреждений, но Comeau предупреждает, что выражение typeid не имеет никакого эффекта.Раздел 5.2.8 в стандарте C ++ не говорит ничего подобного тому, что говорится в комментарии, т. е.typeid вычисляется во время компиляции для неполиморфных типов и во время выполнения для полиморфных типов.

1) Итак, я предполагаю, что комментарий вводит в заблуждение / явно неверен, или поскольку автор этого кода является довольно опытным программистом на C ++, я что-то упускаю?

2) OTOH, мне интересно, можем ли мы проверить, является ли класс полиморфным (имеет хотя бы одну виртуальную функцию), используя какую-либо технику?

3) Когда кто-то хотел бы знать, является ли класс полиморфным?Дикое предположение;чтобы получить начальный адрес класса с помощью dynamic_cast<void*>(T) (как dynamic_cast работает только на полиморфных классах).

Жду вашего мнения.

Заранее спасибо,

Это было полезно?

Решение

Я не могу представить себе никакого возможного способа, как этот typeid можно было бы использовать для проверки того, что тип полиморфен.Его даже нельзя использовать для утверждения, что это так, поскольку typeid будет работать с любым типом.Boost имеет реализацию здесь.Что касается того, почему это может быть необходимо - один известный мне случай - это Boost.Библиотека сериализации.Если вы сохраняете неполиморфный тип, то вы можете просто сохранить его.При сохранении полиморфного типа вы должны получить его динамический тип, используя typeid, а затем вызвать метод сериализации для этого типа (найдя его в какой-нибудь таблице).

Обновить:похоже, я на самом деле ошибаюсь.Рассмотрим этот вариант:

template <class T> 
bool isPolymorphic() { 
    bool answer=false;
    T *t = new T();
    typeid(answer=true,*t); 
    delete t;
    return answer; 
}

Это действительно работает, как следует из названия, точно для каждого комментария в вашем исходном фрагменте кода.Выражение внутри typeid не вычисляется, если оно "не обозначает lvalue типа полиморфного класса" (std 3.2/2).Итак, в приведенном выше случае, если T не является полиморфным, выражение typeid не вычисляется.Если T полиморфно, то * t действительно является значением полиморфного типа, поэтому необходимо вычислить все выражение целиком.

Итак, ваш первоначальный пример по-прежнему неверен :-).Он использовал T(), не *tT() Создать r значение (стандарт 3.10/6).Таким образом, это все еще выдает выражение, которое не является "значением полиморфного класса".

Это довольно интересный трюк.С другой стороны, его практическая ценность несколько ограничена - потому что, хотя boost::is_polymorphic дает вам константу времени компиляции, эта константа дает вам значение времени выполнения, поэтому вы не можете создавать экземпляры разного кода для полиморфных и неполиморфных типов.

Другие советы



class PolyBase {
public:   
    virtual ~PolyBase(){}
};

class NPolyBase {
public:
    ~NPolyBase(){}
};

template<class T>
struct IsPolymorphic
{
    struct Derived : T {
        virtual ~Derived();
    };
    enum  { value = sizeof(Derived)==sizeof(T) };
};


void ff()
{
    std::cout << IsPolymorphic<PolyBase >::value << std::endl;
    std::cout << IsPolymorphic<NPolyBase>::value << std::endl;
}

Начиная с C ++ 11, это теперь доступно в <type_traits> заголовок как std::is_polymorphic.Его можно использовать следующим образом:

struct PolyBase {
  virtual ~PolyBase() {}
};

struct NPolyBase { 
  ~NPolyBase() {}
};

if (std::is_polymorphic<PolyBase>::value)
  std::cout << "PolyBase = Polymorphic\n";
if (std::is_polymorphic<NPolyBase>::value)
  std::cout << "NPolyBase = Also Polymorphic\n";

При этом выводится просто "PolyBase = Полиморфный".

Можно использовать факты, которые:

  1. dynamic_cast сбой во время компиляции, если аргумент не является полиморфным классом.Так что его можно использовать со SFINAE.
  2. dynamic_cast<void*> является допустимым приведением , которое возвращает адрес полный полиморфный объект.

Следовательно, в C ++ 11:

#include <iostream>
#include <type_traits>

template<class T>
auto is_polymorphic2_test(T* p) -> decltype(dynamic_cast<void*>(p), std::true_type{});

template<class T>
auto is_polymorphic2_test(...) -> std::false_type;

template<class T>
using is_polymorphic2 = decltype(is_polymorphic2_test<T>(static_cast<T*>(0)));

struct A {};
struct B { virtual ~B(); };

int main() {
    std::cout << is_polymorphic2<A>::value << '\n'; // Outputs 0.
    std::cout << is_polymorphic2<B>::value << '\n'; // Outputs 1.
}

Я здесь немного запутался и надеюсь получить несколько комментариев к этому ответу, объясняющих, чего мне не хватает.

Конечно, если вы хотите выяснить, является ли класс полиморфным, все, что вам нужно сделать, это спросить, поддерживает ли он dynamic_cast, разве это не так?

template<class T, class> struct is_polymorphic_impl   : false_type {};
template<class T> struct is_polymorphic_impl
    <T, decltype(dynamic_cast<void*>(declval<T*>()))> : true_type {};

template<class T> struct is_polymorphic :
    is_polymorphic_impl<remove_cv_t<T>, void*> {};

Кто-нибудь может указать на недостаток в этой реализации?Я предполагаю, что он должен быть, или, должно быть, был в какой-то момент в прошлом, потому что документация по ускорению продолжает утверждать, что is_polymorphic "не может быть переносимо реализован на языке C ++".

Но "переносимо" - это что-то вроде ласкательного слова, верно?Возможно, они просто ссылаются на то, что MSVC не поддерживает expression-SFINAE, или некоторые диалекты, такие как Embedded C ++, не поддерживают dynamic_cast.Может быть, когда они говорят "язык C ++", они имеют в виду "подмножество языка C ++ с наименьшим общим знаменателем". Но у меня есть ноющее подозрение, что, возможно, они имеют в виду то, что говорят, и I'm тот, кто чего-то не хватает.

В typeid подход в OP (с поправками, внесенными более поздним ответом, для использования lvalue, а не rvalue) также кажется прекрасным, но, конечно, это не constexpr, и это требует фактического построения T, что может быть очень дорого.Так что это dynamic_cast подход кажется лучше...если только это по какой-то причине не сработает.Мысли?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top