我经常遇到这样的情况,我引入了一个抽象基类(称为 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;
}
但是,如果我还想允许迭代homogeneous 类型的 vector (例如 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 &
)和 std::unique_ptr<Type>
(甚至 Type *
或 const Type *
)统一。但是,我还没有看到它在生产代码中被大量使用。这是避免代码重复的常见模式吗?或者有更好的方法来处理这个问题吗?
最佳答案
我会写 get_ptr<T>
.
get_ptr<T*>
如果失败则返回 nullptr。
如果传递了对可转换为 T*
的内容的引用, 它返回它。
如果传递了对可转换为 T&
的内容的引用, 它返回一个指向它的指针。
否则,如果传递一个指针,如果指针为 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
的可能性失败在这里相当明显。
如果你不想硬编码类型 T
,我们可以更进一步。
如果get_obj_ptr
传递一个指向指针的指针,并且指针不为空,它取消引用指针并递归。如果为 null,则返回转换为相同类型的 nullptr。
如果通过了谁的类(class).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;
}
作为一般规则,采用智能指针并假设它们永远不会为 null 是一个坏主意。
关于c++ - 统一迭代 std::vector<T> 和 std::vector<unique_ptr<T>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45286632/