在std :: vector <t>和std :: vector <unique_ptr <t>&gt;上统一迭代

时间:2017-07-24 17:29:33

标签: c++ c++11 vector stl

我通常面临这样的情况:我引入了一个抽象基类(称为Foo)来存储容器中不同子类(称为BarBaz)的实例(例如,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 &)和std::unique_ptr<Type>(或甚至Type *const Type *类型的引用的一般可能性均匀。但是,我还没有看到它在生产代码中被广泛使用。这是避免代码重复的常见模式吗?或者有更好的方法来解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

我写get_ptr<T>

get_ptr<T*>如果失败则返回nullptr。

如果传递了对可转换为T*的内容的引用,则返回它。

如果传递对可转换为T&的内容的引用,则返回指向它的指针。

否则,如果传递了一个指针,如果指针为空则返回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强制转换为相同的类型。

如果传递了.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;
}

作为一般规则,采用智能指针并假设它们永远不为空是一个坏主意。