![](/img/trans.png)
[英]Iterating over const T& in a std::vector<std::unique_ptr<T> >
[英]Iterating uniformly over std::vector<T> and std::vector<unique_ptr<T>>
我通常遇到的情況是,我引入一個抽象基類(稱為Foo
)以將不同子類(稱為Bar
和Baz
)的實例存儲在容器中(例如std::vector<std::unique_ptr<Foo>>
)。 為了說明起見,讓我將這些示例類放在這里:
class Foo {
public:
virtual int getId() const = 0;
};
class Bar : public Foo {
public:
Bar(int id) : id_(id) {}
int getId() const override { return id_; }
private:
int id_;
};
class Baz : public Foo {
public:
Baz(int id) : id_(id) {}
int getId() const override { return id_; }
private:
int id_;
};
如果我實現了一個迭代std::vector<std::unique_ptr<Foo>>
的函數,它看起來像
template<class InputIterator>
void printIds(InputIterator first, InputIterator last) {
for (; first != last; ++first)
std::cout << (*first)->getId() << std::endl;
}
但是,如果我還想允許遍歷同類類型的向量(例如std::vector<Bar>
)而不重寫整個函數(或其他類似類型的函數)怎么辦? 我看到兩種明顯的可能性:
1)實現功能
template<class Type>
const Type & dereference(const Type &value) {
return value;
}
template<class Type>
const Type & dereference(const std::unique_ptr<Type> &value) {
return *value;
}
並更換
std::cout << (*first)->getId() << std::endl;
通過
std::cout << dereference(*first).getId() << std::endl;
2)實現功能
template<class Type>
int getId(const Type &value) {
return value.getId();
}
template<class Type>
int getId(const std::unique_ptr<Type> &value) {
return value->getId();
}
並更換
std::cout << (*first)->getId() << std::endl;
通過
std::cout << getId(*first) << std::endl;
選項1)似乎可以統一處理Type &
(或const Type &
) const Type &
和std::unique_ptr<Type>
(甚至Type *
或const Type *
)的引用。 但是,我沒有看到它在生產代碼中使用得太多。 這是避免代碼重復的常見模式嗎? 還是有更好的方法來解決這個問題?
我會寫get_ptr<T>
。
如果失敗,則get_ptr<T*>
返回nullptr。
如果傳遞了對可轉換為T*
的對象的引用,它將返回它。
如果傳遞了對可轉換為T&
的對象的引用,它將返回指向該對象的指針。
否則,如果傳遞了指針,則如果指針為null則返回null,如果指針不為null則返回get_ptr<T>(*ptr)
。
否則,如果x.get()
返回一個指針,則返回get_ptr<T>(x.get())
。
現在printIds
讀取:
template<class InputIterator>
void printIds(InputIterator first, InputIterator last) {
for (; first != last; ++first)
std::cout << get_ptr<Foo>(*first)->getId() << std::endl;
}
請注意, get_ptr
失敗的get_ptr
在這里非常明顯。
如果您不想對T
類型進行硬編碼,我們可以更進一步。
如果向get_obj_ptr
傳遞了一個指向指針的指針,並且該指針不為null,則它將取消對該指針的引用並遞歸。 如果為null,則返回強制轉換為相同類型的nullptr。
如果傳遞了.get()
返回其指針的類,則get_obj_ptr(x.get())
將在get_obj_ptr(x.get())
上遞歸。
否則,如果將指針傳遞給get_obj_ptr(x)
,則它將返回x
。
否則,如果將左值傳遞給get_obj_ptr(x)
,則它將返回std::addressof(x)
。
與您的代碼不同,它承認失敗的可能性。
template<class InputIterator>
void printIds(InputIterator first, InputIterator last) {
for (; first != last; ++first)
auto* ptr = get_obj_ptr(*first);
if (ptr)
std::cout << ptr->getId() << std::endl;
else
std::cout << "null object" << std::endl;
}
通常,采用智能指針並假定它們永遠不會為空是一個壞主意。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.