Сообщение Re[7]: Приведение типов итераторов для контейнера с базовым от 17.01.2024 9:04
Изменено 17.01.2024 9:10 rg45
Re[7]: Приведение типов итераторов для контейнера с базовым
Здравствуйте, drVanо, Вы писали:
R>>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты.
V>Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
Ну это уже зависит от потребностей — смотря, что нужно получить на выходе. Вообще, этих всяких адаптеров можно напридумывать вагон и маленькую тележку (что собственно уже и сделано в boost). И имеет смысл вместо сложных монолитных адаптеров делать маленькие простые шаблончики, а потом их комбинировать как конструктор лего. В примере ниже определены два простых адаптора: IndirectIterator и StaticCastIterator. Эти два адаптора комбинируются (StaticCastIterator поверх IndirectIterator) и применяются к инкапсулированному контейнеру std::vector<std::unique_ptr<T>>. В принципе, вместо unique_ptr можно подставить любой другой вид указателя и будет работать точно так же. В примере присутствует избыточный копи-пастинг — это ради того, чтоб не перегружать пример дополнительными сущностями и не подключать boost. По-хорошему, такого быть не должно, конечно же.
http://coliru.stacked-crooked.com/a/8764c68363edc011
Output:
R>>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты.
V>Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
Ну это уже зависит от потребностей — смотря, что нужно получить на выходе. Вообще, этих всяких адаптеров можно напридумывать вагон и маленькую тележку (что собственно уже и сделано в boost). И имеет смысл вместо сложных монолитных адаптеров делать маленькие простые шаблончики, а потом их комбинировать как конструктор лего. В примере ниже определены два простых адаптора: IndirectIterator и StaticCastIterator. Эти два адаптора комбинируются (StaticCastIterator поверх IndirectIterator) и применяются к инкапсулированному контейнеру std::vector<std::unique_ptr<T>>. В принципе, вместо unique_ptr можно подставить любой другой вид указателя и будет работать точно так же. В примере присутствует избыточный копи-пастинг — это ради того, чтоб не перегружать пример дополнительными сущностями и не подключать boost. По-хорошему, такого быть не должно, конечно же.
http://coliru.stacked-crooked.com/a/8764c68363edc011
#include <vector>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>
template <typename BaseIterator>
class IndirectIterator
{
public:
using TargetType = decltype(**std::declval<BaseIterator>());
IndirectIterator() = default;
IndirectIterator(const BaseIterator& i) : m_baseIterator(i) {}
TargetType operator*() const { return **m_baseIterator; }
bool operator == (const IndirectIterator& rhs) const { return m_baseIterator == rhs.m_baseIterator; }
bool operator != (const IndirectIterator& rhs) const { return !(*this == rhs); }
IndirectIterator& operator++() { ++m_baseIterator; return *this; }
IndirectIterator operator++(int) { return m_baseIterator++; }
private:
BaseIterator m_baseIterator;
};
template <typename BaseIterator, typename TargetType>
class StaticCastIterator
{
public:
StaticCastIterator() = default;
StaticCastIterator(const BaseIterator& i) : m_baseIterator(i) {}
TargetType operator*() const { return static_cast<TargetType>(*m_baseIterator); }
bool operator == (const StaticCastIterator& rhs) const { return m_baseIterator == rhs.m_baseIterator; }
bool operator != (const StaticCastIterator& rhs) const { return !(*this == rhs); }
StaticCastIterator& operator++() { ++m_baseIterator; return *this; }
StaticCastIterator operator++(int) { return m_baseIterator++; }
private:
BaseIterator m_baseIterator;
};
template <typename T>
class BaseList
{
using Items_ = std::vector<std::unique_ptr<T>>;
public:
using iterator = IndirectIterator<typename Items_::iterator>;
using const_iterator = IndirectIterator<typename Items_::const_iterator>;
iterator begin() { return items_.begin(); }
iterator end() { return items_.end(); }
const_iterator begin() const { return items_.begin(); }
const_iterator end() const { return items_.end(); }
protected:
Items_ items_;
};
class BaseImport {};
template <typename T>
class ImportList : public BaseList<BaseImport>
{
public:
// Use concepts instead of the static_assert on C++20
static_assert(std::is_base_of_v<BaseImport, T>);
using BaseList = BaseList<BaseImport>;
using iterator = StaticCastIterator<typename BaseList::iterator, T&>;
using const_iterator = StaticCastIterator<typename BaseList::const_iterator, const T&>;
iterator begin() { return BaseList::begin(); }
iterator end() { return BaseList::end(); }
const_iterator begin() const { return BaseList::begin(); }
const_iterator end() const { return BaseList::end(); }
template <typename... CreationParams>
void CreateItem(CreationParams&&... params) {
BaseList::items_.emplace_back(std::make_unique<T>(std::forward<CreationParams>(params)...));
}
};
struct MacImport : BaseImport
{
int id{};
std::string name;
MacImport(int id, const std::string& name) : id(id), name(name){}
};
int main()
{
using MacImportList = ImportList<MacImport>;
MacImportList l;
l.CreateItem(101, "Mac Item 101");
l.CreateItem(102, "Mac Item 102");
l.CreateItem(103, "Mac Item 103");
for (const MacImport& item : l)
{
std::cout << "[" << item.id << "]: " << item.name << std::endl;
}
}
Output:
[101]: Mac Item 101
[102]: Mac Item 102
[103]: Mac Item 103
Re[7]: Приведение типов итераторов для контейнера с базовым
Здравствуйте, drVanо, Вы писали:
R>>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты.
V>Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
Ну это уже зависит от потребностей — смотря, что нужно получить на выходе. Вообще, этих всяких адаптеров можно напридумывать вагон и маленькую тележку (что собственно уже и сделано в boost). И имеет смысл вместо сложных монолитных адаптеров делать маленькие простые шаблончики, а потом их комбинировать как конструктор лего. В примере ниже определены два простых адаптора: IndirectIterator и StaticCastIterator. Эти два адаптора комбинируются (StaticCastIterator поверх IndirectIterator) и применяются к инкапсулированному контейнеру std::vector<std::unique_ptr<T>>. В принципе, вместо unique_ptr можно подставить любой другой вид указателя и будет работать точно так же. В примере присутствует избыточный копи-пастинг — это ради того, чтоб не перегружать пример дополнительными сущностями и не подключать boost. По-хорошему, такого быть не должно, конечно же (для этого и придуманы шаблоны boost::iterator_facade и boost::iterator_adaptor).
http://coliru.stacked-crooked.com/a/8764c68363edc011
Output:
R>>Да, еще один момент: адапторы итераторов, на самом деле, классная штука, с помощью которой можно адаптировать разного рода коллекции под конкретные нужды. Например, ты можешь подпилить этот адаптор так, чтоб он возвращал не указатели, а сразу ссылки на объекты.
V>Можно какой-нибудь простой пример такого адаптора к std::vector<std::unique_ptr<T>>?
Ну это уже зависит от потребностей — смотря, что нужно получить на выходе. Вообще, этих всяких адаптеров можно напридумывать вагон и маленькую тележку (что собственно уже и сделано в boost). И имеет смысл вместо сложных монолитных адаптеров делать маленькие простые шаблончики, а потом их комбинировать как конструктор лего. В примере ниже определены два простых адаптора: IndirectIterator и StaticCastIterator. Эти два адаптора комбинируются (StaticCastIterator поверх IndirectIterator) и применяются к инкапсулированному контейнеру std::vector<std::unique_ptr<T>>. В принципе, вместо unique_ptr можно подставить любой другой вид указателя и будет работать точно так же. В примере присутствует избыточный копи-пастинг — это ради того, чтоб не перегружать пример дополнительными сущностями и не подключать boost. По-хорошему, такого быть не должно, конечно же (для этого и придуманы шаблоны boost::iterator_facade и boost::iterator_adaptor).
http://coliru.stacked-crooked.com/a/8764c68363edc011
#include <vector>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>
template <typename BaseIterator>
class IndirectIterator
{
public:
using TargetType = decltype(**std::declval<BaseIterator>());
IndirectIterator() = default;
IndirectIterator(const BaseIterator& i) : m_baseIterator(i) {}
TargetType operator*() const { return **m_baseIterator; }
bool operator == (const IndirectIterator& rhs) const { return m_baseIterator == rhs.m_baseIterator; }
bool operator != (const IndirectIterator& rhs) const { return !(*this == rhs); }
IndirectIterator& operator++() { ++m_baseIterator; return *this; }
IndirectIterator operator++(int) { return m_baseIterator++; }
private:
BaseIterator m_baseIterator;
};
template <typename BaseIterator, typename TargetType>
class StaticCastIterator
{
public:
StaticCastIterator() = default;
StaticCastIterator(const BaseIterator& i) : m_baseIterator(i) {}
TargetType operator*() const { return static_cast<TargetType>(*m_baseIterator); }
bool operator == (const StaticCastIterator& rhs) const { return m_baseIterator == rhs.m_baseIterator; }
bool operator != (const StaticCastIterator& rhs) const { return !(*this == rhs); }
StaticCastIterator& operator++() { ++m_baseIterator; return *this; }
StaticCastIterator operator++(int) { return m_baseIterator++; }
private:
BaseIterator m_baseIterator;
};
template <typename T>
class BaseList
{
using Items_ = std::vector<std::unique_ptr<T>>;
public:
using iterator = IndirectIterator<typename Items_::iterator>;
using const_iterator = IndirectIterator<typename Items_::const_iterator>;
iterator begin() { return items_.begin(); }
iterator end() { return items_.end(); }
const_iterator begin() const { return items_.begin(); }
const_iterator end() const { return items_.end(); }
protected:
Items_ items_;
};
class BaseImport {};
template <typename T>
class ImportList : public BaseList<BaseImport>
{
public:
// Use concepts instead of the static_assert on C++20
static_assert(std::is_base_of_v<BaseImport, T>);
using BaseList = BaseList<BaseImport>;
using iterator = StaticCastIterator<typename BaseList::iterator, T&>;
using const_iterator = StaticCastIterator<typename BaseList::const_iterator, const T&>;
iterator begin() { return BaseList::begin(); }
iterator end() { return BaseList::end(); }
const_iterator begin() const { return BaseList::begin(); }
const_iterator end() const { return BaseList::end(); }
template <typename... CreationParams>
void CreateItem(CreationParams&&... params) {
BaseList::items_.emplace_back(std::make_unique<T>(std::forward<CreationParams>(params)...));
}
};
struct MacImport : BaseImport
{
int id{};
std::string name;
MacImport(int id, const std::string& name) : id(id), name(name){}
};
int main()
{
using MacImportList = ImportList<MacImport>;
MacImportList l;
l.CreateItem(101, "Mac Item 101");
l.CreateItem(102, "Mac Item 102");
l.CreateItem(103, "Mac Item 103");
for (const MacImport& item : l)
{
std::cout << "[" << item.id << "]: " << item.name << std::endl;
}
}
Output:
[101]: Mac Item 101
[102]: Mac Item 102
[103]: Mac Item 103