Сначала у меня есть несколько замечаний:
if (!DuplicatesExist<T>())
Что вы хотите проверить? Если ваш указатель является дубликатом, поскольку он указывает на тот же экземпляр? Если это так, вы можете напрямую использовать std::set
вместо вектора!
m_components.emplace_back(std::make_unique<T>(*this));
Это создаст новый объект как копию объекта "this". Таким образом, вы всегда будете получать новый объект и указатель, который содержит адрес того, который никогда не будет равен другому! Это ваше намерение?
И если в этом случае m_components
является членом самого производного класса, хотим ли мы получить полную копию всех элементов? Здесь что-то очень загадочное: -)
ОК, но вернемся к вопросу:
Одно решение: используйте свой собственный «тег» ручной работы для каждого типа. Этот код немного велик и не очень удобен:
class Base
{
public:
enum Id { ONE, TWO };
virtual Id GetId() const = 0;
};
class A: public Base
{
public:
static constexpr Id id = ONE;
Id GetId() const override { return id; }
std::string someParm;
void AFunc() { std::cout << "A-Func" << someParm << std::endl; }
A( const std::string& parm ): someParm{ parm } {}
};
class B: public Base
{
public:
static constexpr Id id = TWO;
Id GetId() const override { return id; }
void BFunc() { std::cout << "B-Func" << std::endl; }
};
template < typename T, typename CONTAINER >
T* Get( CONTAINER &objects )
{
auto it = std::find_if( objects.begin(), objects.end(), []( std::unique_ptr<Base>& ptr ) { return ptr->GetId() == T::id; } );
if ( it != objects.end() ) return static_cast<T*>( it->get() );
return nullptr;
}
int main()
{
std::vector< std::unique_ptr<Base> > objects;
objects.emplace_back( std::make_unique<A> ("Hallo") );
objects.emplace_back( std::make_unique<B> () );
auto ptra = Get< A >( objects );
if ( ptra ) ptra->AFunc();
auto ptrb = Get< B >( objects );
if ( ptrb ) ptrb->BFunc();
}
Или вы используете dynamic_cast
, для которого требуется как минимум одна виртуальная функция в вашем базовом классе и включена поддержка RTTI для вашего компилятора, что обычно невозможно на маленькие встроенные устройства!
class Base
{
virtual void SomeFunc() {};
};
class A: public Base
{
public:
std::string someParm;
void AFunc() { std::cout << "A-Func" << someParm << std::endl; }
A( const std::string& parm ): someParm{ parm } {}
};
class B: public Base
{
public:
void BFunc() { std::cout << "B-Func" << std::endl; }
};
template < typename T, typename CONTAINER >
T* Get( CONTAINER &objects )
{
T* ret;
for ( auto& ptr: objects )
{
Base* base = ptr.get();
ret = dynamic_cast<T*>( base );
if ( ret ) return ret;
}
return nullptr;
}
int main()
{
std::vector< std::unique_ptr<Base> > objects;
objects.emplace_back( std::make_unique<A> ("Hallo") );
objects.emplace_back( std::make_unique<B> () );
auto ptra = Get< A >( objects );
if ( ptra ) ptra->AFunc();
auto ptrb = Get< B >( objects );
if ( ptrb ) ptrb->BFunc();
}
или используйте std::variant
class A
{
public:
std::string someParm;
void AFunc() { std::cout << "A-Func" << someParm << std::endl; }
A( const std::string& parm ): someParm{ parm } {}
};
class B
{
public:
void BFunc() { std::cout << "B-Func" << std::endl; }
};
using VAR_T = std::variant< std::unique_ptr<A>, std::unique_ptr<B> >;
int main()
{
std::vector< VAR_T > objects;
objects.emplace_back( std::make_unique<A> ("Hallo") );
objects.emplace_back( std::make_unique<B> () );
for ( auto& ptr: objects )
{
std::visit( []( auto& lptr )
{
using T = std::decay_t<decltype(lptr)>;
if constexpr ( std::is_same< T, std::unique_ptr<A> >::value )
{
lptr->AFunc();
}
else if constexpr ( std::is_same< T, std::unique_ptr<B> >::value )
{
lptr->BFunc();
}
}
, ptr );
}
}
Последний способ кажется мне самым простым. Это происходит без особых дополнительных затрат. Внутри варианта хранится только «тег», но, как вариант, содержащий только разные указатели одинакового размера, здесь нет никаких дополнительных затрат для размера варианта. И вы все еще можете использовать его на небольших встроенных устройствах, так как вам не нужен RTTI. Кроме того, вам не нужна виртуальная функция, и вам также не нужно наследовать от базового класса. Так что это очень гибкий!