que - polimorfismo puro c++
Clase de plantilla que tiene todas las funciones virtuales en lĂnea (1)
Su código (la versión completa, con dos archivos de cabecera y main.C) compila y enlaces sin ningún error para mí, con gcc 4.8.3, con las opciones predeterminadas (excepto para -std = c ++ 11, para habilitar C + Modo +11).
Incluso cargué el ejecutable resultante en gdb. gdb se lo tragó sin problemas.
No veo nada mal aquí.
Estoy usando plantillas de clase que contienen funciones virtuales en mi proyecto actual, y me encontré con un problema que no puedo superar por mi cuenta.
- Las plantillas de clase no pueden tener sus cuerpos de funciones miembro divididos de la definición de clase en el archivo .hpp debido a errores del enlazador. No quiero crear instancias de mis plantillas para cada tipo nuevo que estoy a punto de usar, así que todo lo que queda es dejarlos en línea. Esto está absolutamente bien, ya que tienen de 1 a 2 líneas la mayor parte del tiempo, por lo que no voy a experimentar ninguna inflamación de código.
- Por otro lado, gcc crea vtable para una clase polimórfica en el archivo .cpp que tiene la definición de la primera función no en línea que se declara en la definición de la clase. Como tengo todas las funciones de miembro en línea, obtengo una referencia indefinida a vtable, o no encontré ningún símbolo de RTTI para mi clase en GDB.
Por favor considere el siguiente código:
template <typename T>
struct Test
{
virtual void testMe() const = 0;
virtual ~Test() = default;
};
template <typename T>
struct test : public Test<T>
{
virtual void testMe() const
{
std::cout << typeid(T).name() << std::endl;
}
virtual ~test() = default;
};
int main()
{
test<int> t;
Test<int>& T = t;
T.testMe();
return 0;
}
En este ejemplo particular estoy obteniendo:
can''t find linker symbol for virtual table for `test<int>'' value
al depurar con GDB.
¿Cómo obligo a mi compilador a poner vtable en un archivo cpp específico cuando todas las funciones de clase están en línea?
EDITAR :
Como el ejemplo anterior no ilustra el problema, aquí está mi código original.
La clase que está causando el problema:
#ifndef CONVERTIBLETO_H
#define CONVERTIBLETO_H
#include "convertibleTo_converters.h"
#include <functional>
template <
typename IT,
template <typename InterfaceType, typename ErasedType>
class Converter = convertibleTo_detail::default_converter
>
class convertibleTo
{
public:
typedef convertibleTo<IT, Converter> this_type;
typedef IT InterfaceType;
struct is_type_eraser_tag {};
private:
class holder_interface
{
public:
virtual InterfaceType get() const = 0;
virtual void set(const InterfaceType&) = 0;
virtual holder_interface* clone() const = 0;
virtual ~holder_interface() {}
};
template <typename ErasedType>
class holder : public holder_interface
{
public:
virtual InterfaceType get() const
{
return (Converter<InterfaceType, ErasedType>::convert(this->data));
}
virtual void set(const InterfaceType& value)
{
this->data = (Converter<InterfaceType, ErasedType>::convert(value));
}
virtual holder_interface* clone() const
{
return new holder(*this);
}
holder() = delete;
holder(const holder& other):
data(other.data)
{ }
holder(ErasedType& d):
data(d)
{ }
virtual ~holder() = default;
private:
ErasedType& data;
};
public:
inline InterfaceType get() const
{
if (this->held)
return this->held->get();
else
return InterfaceType();
}
inline void set(const InterfaceType& value)
{
if (this->held)
this->held->set(value);
}
inline bool empty() const
{
return ! this->held;
}
convertibleTo<InterfaceType, Converter>& operator= (const convertibleTo<InterfaceType, Converter>& other)
{
if(this->held)
delete this->held;
this->held = other.held->clone();
return *this;
}
convertibleTo():
held(nullptr)
{ }
template <typename T>
explicit convertibleTo(T& data):
held(new holder<T>(data))
{
}
convertibleTo( convertibleTo& other ):
convertibleTo( const_cast<const convertibleTo&>(other))
{
}
convertibleTo( const convertibleTo& other ):
held(nullptr)
{
if(other.held)
this->held = other.held->clone();
}
~convertibleTo()
{
if (this->held)
delete this->held;
}
private:
holder_interface * held;
};
#endif
Clases de ayudantes requeridas:
#ifndef CONVERTIBLETO_CONVERTERS_H
#define CONVERTIBLETO_CONVERTERS_H
#include <string>
#include <sstream>
namespace convertibleTo_detail
{
template <typename InterfaceType, typename ErasedType>
struct default_converter
{
static inline InterfaceType convert(const ErasedType& input)
{
return input;
}
static inline ErasedType convert(const InterfaceType& input)
{
return input;
}
};
template <typename T>
struct default_converter<T, T>
{
static inline T convert(const T& input)
{
return input;
}
};
template <typename ErasedType>
struct default_converter<std::string, ErasedType>
{
static inline std::string convert(const ErasedType& input)
{
default_converter<std::string, ErasedType>::prepareConverter();
default_converter<std::string, ErasedType>::converter << input;
return default_converter<std::string, ErasedType>::converter.str();
}
static inline ErasedType convert(const std::string& input)
{
default_converter<std::string, ErasedType>::prepareConverter(input);
ErasedType result;
default_converter<std::string, ErasedType>::converter >> result;
return result;
}
private:
static std::stringstream converter;
struct SetExceptionFlagsOnce
{
SetExceptionFlagsOnce()
{
default_converter<std::string, ErasedType>::converter.exceptions(std::stringstream::failbit);
}
};
static void inline prepareConverter(std::string value = "")
{
static SetExceptionFlagsOnce setter;
default_converter<std::string, ErasedType>::converter.clear();
default_converter<std::string, ErasedType>::converter.str(value);
}
};
template <typename ErasedType>
std::stringstream default_converter<std::string, ErasedType>::converter;
template <>
struct default_converter<std::string, std::string>
{
static inline std::string convert(const std::string& input)
{
return input;
}
};
}
#endif // CONVERTIBLETO_CONVERTERS_H
main.cpp:
#include <iostream>
#include "convertibleTo.h"
int main()
{
int I = 5;
convertibleTo< std::string > i(I);
std::cout << i.get() << std::endl;
i.set("321");
std::cout << i.get() << std::endl;
return 0;
}
El error que estoy obteniendo es:
RTTI symbol not found for class ''convertibleTo<std::string, convertibleTo_detail::default_converter>::holder<int>''
se muestra cuando entro en i.get (), y luego en el interior del contenedor get ().
EDITAR : movió la fuente completa de pastebin aquí, según sugerencia
Dado que los dos últimos comentarios sugirieron que esto es un error de GDB, ¿cómo lo verifico yo la próxima vez?
- En caso de que GDB se queje de vtable faltante, ¿sería suficiente confirmar que puedo acceder a cada miembro virtual mediante una referencia a ABC inicializado con clase derivada para confirmar que todo está bien?
- En caso de que GDB se queje del símbolo RTTI faltante, ¿sería suficiente con llamar a typeid () en la referencia a ABC inicializado con clase derivada para confirmar que el símbolo RTTI está presente?