Implementando is_base_of com Sobrecarga de Funções Membro em C++

Uso Básico

A seguir, apresentamos um exemplo simples demonstrando o funcionamento do std::is_base_of:

#include "killCmake.h"
#include <string>

using namespace std;

class ClasseBase
{
};

class ClasseDerivada : public ClasseBase
{
public:
    ClasseDerivada(int valor) : valor_(valor)
    {}
private:
    int valor_;
};

int main()
{
    cout << std::is_base_of<classebase classebase="">::value << endl;
    cout << std::is_base_of<classederivada classebase="">::value << endl;
    cout << std::is_base_of<classebase classederivada="">::value << endl;

    return 0;
}
</classebase></classederivada></classebase></string>

Variável Template no C++17

A partir do C++17, foi introduzida a variável template is_base_of_v que simplifica a sintaxe de utilização:

#include "killCmake.h"
#include <string>

using namespace std;

class ClasseBase
{
};

class ClasseDerivada : public ClasseBase
{
public:
    ClasseDerivada(int valor) : valor_(valor)
    {}
private:
    int valor_;
};

template<class base="" class="" derived="">
inline constexpr bool is_base_of_v = std::is_base_of<base derived=""></base>::value;

int main()
{
    cout << std::is_base_of<classebase classebase="">::value << endl;
    cout << std::is_base_of<classederivada classebase="">::value << endl;
    cout << std::is_base_of<classebase classederivada="">::value << endl;

    cout << endl;
    
    cout << is_base_of_v<classebase classebase=""> << endl;
    cout << is_base_of_v<classederivada classebase=""> << endl;
    cout << is_base_of_v<classebase classederivada=""> << endl;

    return 0;
}
</classebase></classederivada></classebase></classebase></classederivada></classebase></class></string>

Implementação Personalizada

Podemos implementar nossa própria versão de is_base_of utilizando sobrecarga de funções membro. A técnica empregada utiliza SFINAE para determinar a relação de herança:

#include "killCmake.h"
#include <string>
#include <type_traits>

using namespace std;

class ClasseBase
{
};

class ClasseDerivada : public ClasseBase
{
public:
    ClasseDerivada(int valor) : valor_(valor)
    {}
private:
    int valor_;
};

template<typename base="" derived="" typename="">
class VerificaHeranca
{
private:
    template<typename t="">
    static std::true_type verificar(T*);

    template<typename>
    static std::false_type verificar(void*);

    template<typename b="" d="" typename="">
    static auto avaliacao() -> decltype(verificar<b>(static_cast<d>(nullptr)));

public:
    static constexpr bool value = std::is_same_v<
        std::integral_constant<bool std::is_class_v=""> && 
            std::is_class_v<derived> && 
            decltype(avaliacao<base derived=""></base>())::value>,
        std::integral_constant<bool true="">
    >;
};

template<class base="" class="" derived="">
inline constexpr bool is_base_of_v = VerificaHeranca<base derived=""></base>::value;

int main()
{
    cout << std::is_base_of<classebase classebase="">::value << endl;
    cout << std::is_base_of<classederivada classebase="">::value << endl;
    cout << std::is_base_of<classebase classederivada="">::value << endl;

    cout << endl;
    
    cout << is_base_of_v<classebase classebase=""> << endl;
    cout << is_base_of_v<classederivada classebase=""> << endl;
    cout << is_base_of_v<classebase classederivada=""> << endl;

    return 0;
}
</classebase></classederivada></classebase></classebase></classederivada></classebase></class></bool></derived></bool></d></b></typename></typename></typename></typename></type_traits></string>

Explicação da Implementação

A implementação personalizada VerificaHeranca funciona da seguinte maneira:

  • A função membro verificar possui duas sobrecargas: uma que aceita ponteiros para qualquer tipo e retorna true_type, e outra que aceita apenas void* e retorna false_type.
  • Quando Base é efetivamente uma classe base de Derived, a convresao de Derived* para Base* é válida, permitindo que a primeira sobrecarga seja selecionada.
  • A verificação adicional garante que tanto Base quanto Derived sejam classes (não tipos primitivos).
  • A comparação com std::is_same garante que o resultado seja booleano verdadeiro apenas quando a verificação de herança for bem-sucedida.

Publicado em 7-4 00:43