Pergunta

Temos 'commonUtils' um sub-projeto que tem muitos code-snippets genéricos utilizados em todo o projeto pai. Uma dessas coisas interessantes que eu vi foi: -

/*********************************************************************
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; 
}

Eu acreditava que o comentário e pensei que é bastante um modelo interessante embora não seja usado em todo o projeto. Eu tentei usá-lo como esta só por curiosidade ...

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";

Mas nenhum deles jamais retorna verdadeiro. MSVC 2005 dá nenhum aviso, mas Comeau adverte expressão typeid não tem efeito. Seção 5.2.8 no C ++ padrão não diz nada parecido com o que diz o comentário ou seja typeid está é avaliada em tempo de compilação para tipos não polimórficos e em tempo de execução para tipos polimórficos.

1) Então eu acho que o comentário é enganosa / plain-errado ou desde que o autor deste código é bastante C sênior ++ programador, estou faltando alguma coisa?

2) OTOH, eu estou querendo saber se podemos testar se uma classe é polimórfico (tem pelo menos uma função virtual) usando alguma técnica?

3) Em que um quer saber se uma classe é polimórfica? Palpite ousado; para obter o start-endereço de uma classe usando dynamic_cast<void*>(T) (como dynamic_cast funciona apenas em classes polimórficas).

Aguardando as suas opiniões.

Agradecemos antecipadamente,

Foi útil?

Solução

Eu não posso imaginar qualquer maneira possível como isso typeid poderia ser usado para verificar que tipo é polimórfico. Ele não pode ser usado até mesmo para afirmar que ele é, desde typeid irá funcionar em qualquer tipo. Impulso tem uma implementação aqui . Quanto à razão, pode ser necessário - um caso que conheço é a biblioteca Boost.Serialization. Se você está salvando tipo não polimórficos, então você pode apenas salvá-lo. Se salvar um polimórfico, você tem que recebe o seu tipo dinâmico usando typeid, e em seguida, chamar método de serialização para esse tipo (olhando-o em alguma tabela).

Atualizar : parece que eu sou realmente errado. Considere esta variante:

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

Este realmente funciona como o nome sugere, exatamente por comentário no seu trecho de código original. A expressão dentro typeid não é avaliada se for "não designa uma Ivalue de tipo classe polimórfico" (STD 3,2 / 2). Assim, no caso acima, se T não é polimórfico, a expressão typeid não é avaliada. Se T é polimórfica, então * t é realmente lValue do tipo polimórfico, por isso expressão inteira tem que ser avaliado.

Agora, o seu exemplo original ainda está errado :-). Ele T() usado, não *t. E T() criar rvalue (STD 3.10 / 6). Então, ele ainda produz uma expressão que não é "lvalue de classe polimórfica".

Isso é truque bastante interessante. Por outro lado, seu valor prático é um pouco limitado - porque enquanto boost :: is_polymorphic dá-lhe uma constante em tempo de compilação, este dá-lhe um valor de tempo de execução, para que você não pode instanciar código diferente para tipos polimórficos e não polimórficos .

Outras dicas



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;
}

Desde C ++ 11, este está agora disponível no cabeçalho <type_traits> como std::is_polymorphic. Ele pode ser usado como este:

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";

Isto imprime apenas "polybase = polimórficos".

Pode-se usar os fatos que:

  1. dynamic_cast falhar em tempo de compilação, se o argumento não é uma classe polimórfica. Para que ele possa ser usado com SFINAE.
  2. dynamic_cast<void*> é um elenco válida que retorna o endereço do completo objeto polymorpic.

Assim, em 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.
}

Estou um pouco confuso aqui, e estou esperando para obter alguns comentários sobre esta resposta explicando o que eu estou sentindo falta.

Certamente, se você quiser saber se uma classe é polimórfica, tudo que você tem a fazer é perguntar se ele suporta dynamic_cast, não é mesmo?

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*> {};

Alguém pode apontar uma falha neste implementação? Eu imagino que deve haver um, ou deve ter sido um em algum ponto no passado, porque a documentação impulso continua a afirmar que is_polymorphic "não pode ser portably implementado na linguagem C ++".

Mas "portably" é uma espécie de palavra doninha, certo? Talvez eles estão apenas aludindo à forma como MSVC não suporta expressão-SFINAE, ou alguns dialetos, como Incorporado C ++ não dynamic_cast apoio. Talvez quando eles dizem "a linguagem C ++" que significa "um subconjunto menor denominador comum da linguagem C ++." Mas eu tenho uma suspeita persistente de que talvez eles querem dizer o que dizem, e Eu sou o único que está faltando alguma coisa.

A abordagem typeid no OP (alterada por uma resposta mais tarde usar um lvalue não um rvalue) também parece muito bem, mas é claro que não é constexpr e que exige realmente construir um T, que pode ser super caro. Portanto, esta abordagem dynamic_cast parece melhor ... a menos que ele não funciona por algum motivo. Pensamentos?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top