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
verificarpossui duas sobrecargas: uma que aceita ponteiros para qualquer tipo e retornatrue_type, e outra que aceita apenasvoid*e retornafalse_type. - Quando
Baseé efetivamente uma classe base deDerived, a convresao deDerived*paraBase*é válida, permitindo que a primeira sobrecarga seja selecionada. - A verificação adicional garante que tanto
BasequantoDerivedsejam classes (não tipos primitivos). - A comparação com
std::is_samegarante que o resultado seja booleano verdadeiro apenas quando a verificação de herança for bem-sucedida.