(C ++)用于检查对象是否在vector / array / list / ...中的模板?

时间:2015-02-24 20:35:29

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

是否可以在C ++(11)中创建一个模板,用于检查对象是否包含在std::vectorstd::arraystd::list中的函数(甚至可能更多)容器类型)?

我现在拥有的东西:

typedef std::shared_ptr<Tag> SharedTag;
typedef std::vector<SharedTag> TagList;

bool
Tag::isIn(const TagList& lst) {
    return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
        return t->name == this->name;
    });
}

Tag是正常class。当然,比较应该t == this,稍后将是operator==。为简单起见,我没有在此处添加此内容。

那么,是否可以只为std::vectorstd::arraystd::list(可能是std::set)编写一次高级代码(不包括typedef)等等?

我找不到所有这些类的基类型,......这将是我的第一个想法...

4 个答案:

答案 0 :(得分:5)

选项1(好):直接使用std::find

std::vector<int> v; // populate v however you want
std::vector<int>::const_iterator i = std::find(v.cbegin(), v.cend(), 42);
if (i != v.end()) {
    // Now you know 42 is in v
} else {
    // Now you know 42 is not in v
}

选项2(更好):将std::find包装在辅助函数中:

template <typename Container, typename Value>
bool contains(const Container& c, const Value& v)
{
    return std::find(std::begin(c), std::end(c), v) != std::begin(c);
}

// Example usage:
std::vector<int> v; // populate v however you want
if (contains(v, 42)) {
    // You now know v contains 42
}

选项3(最佳):使用提供一个容器的find方法(对于已排序的容器更快,如set),而std::find用于不提供容器的容器提供一个:

// If you want to know why I added the int and long parameter,
// see this answer here: http://stackoverflow.com/a/9154394/1287251

template <typename Container, typename Value>
inline auto contains(const Container& c, const Value& v, int) -> decltype(c.find(v), bool()) {
    return c.find(v) != std::end(c);
}

template <typename Container, typename Value>
inline bool contains(const Container& c, const Value& v, long) {
    return std::find(std::begin(c), std::end(c), v) != std::end(c);
}

template <typename Container, typename Value>
bool contains(const Container& c, const Value& v) {
    return contains(c, v, 0);
}

// Example usage:
std::set<int> s; // populate s however you want
if (contains(s, 42)) {
    // You now know s contains 42
}

当然,您可以自己编写std::find,但也可以使用它。

答案 1 :(得分:2)

您可以使用模板:

typedef std::shared_ptr<Tag> SharedTag;

template <typename Container>
bool Tag::isIn(const Container& lst) {
    return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
        return t->name == this->name;
    });
}

这要求Container是可转换为SharedTag的东西的容器。

答案 2 :(得分:1)

这些容器之间没有共同的基类型。这不是STL库的工作方式,而是基于模板和通用编程原理。

因此,如果要为所有容器实现一次该函数,则必须将其作为模板。这是一个基本形式:

template <typename TagContainer>
bool Tag::isIn(const TagContainer& lst) {
  return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
    return t->name == this->name;
  });
};

但是这有一个问题,你可以在技术上将任何东西传递给这个实际上不是SharedTag的容器的函数,因此,为了解决这个问题,你可以使用一个名为Sfinae的技巧来强制执行该规则:

template <typename TagContainer>
typename std::enable_if< std::is_same< SharedTag, typename TagContainer::value_type >::value,
bool >::type Tag::isIn(const TagContainer& lst) {
  return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
    return t->name == this->name;
  });
};

哪种丑陋,但它有效。

但仍有一个问题。我怀疑你的Tag类是一个普通的非模板类,这意味着你可能在cpp文件中实现它,但模板需要在头文件中实现(因为函数模板需要实现它们编译器可以看到它为你调用它的每种类型生成一个新的具体版本。

避免这个问题的一种方法是为你想要支持的每个容器提供一些重载的非模板函数,然后,在引擎盖下,你调用一个本地函数模板,在这种情况下,你不需要sfinae技巧来约束它,因为它已经局限于你提供的一组重载。像这样:

template <typename TagContainer>
bool Tag::isIn_impl(const TagContainer& lst) {
  return std::any_of(lst.begin(), lst.end(), [this](const SharedTag& t) {
    return t->name == this->name;
  });
};

bool Tag::isIn(const std::list<SharedTag>& lst) {
  return isIn_impl(lst);
};

bool Tag::isIn(const std::vector<SharedTag>& lst) {
  return isIn_impl(lst);
};

bool Tag::isIn(const std::set<SharedTag>& lst) {
  return isIn_impl(lst);
};

请注意,isIn_impl是一个成员函数模板,应该在头文件中,在类的私有部分中声明,并且可以安全地定义在cpp文件中,因为该cpp文件是唯一从中调用该函数模板的地方。

该解决方案的一个明显问题是您必须手动提供您想要支持的每个重载,这意味着它在将来不是非常“可扩展”,但在现实生活中,可能没有您想要支持的许多容器。如果你想要完全的通用性,你真的必须使用模板方法(除非你想对容器进行类型擦除......但这有点超出了我愿意在这里解释的范围)。

答案 3 :(得分:1)

您可以使用嵌套的可变参数模板来实现此目的。这是一个方便的演示:请注意神奇的部分template <template <typename...> class V, typename E>。变量模板是必需的,因为vectorlist&amp; co。所有模板参数都有不同的数量(分配器,比较器等),STL提供了默认值。

#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <list>
#include <set>
#include <iostream>

class Tag {
public:
    Tag(const std::string &n): name(n) {}

    template <template <typename...> class V, typename E>
    bool isIn(const V<E> &lst) {
        return std::any_of(lst.begin(), lst.end(), [this](const E &t) {
            return t.name == this->name;
        });
    }

private:
    std::string name;
};

typedef std::shared_ptr<Tag> SharedTag;
typedef std::vector<SharedTag> TagList;

int main() {
    Tag t("foo");

    // Set needs some extra bits to work (a `<` operator etc.)
    //std::set<Tag> a = {Tag("foo"), Tag("bar")}; 
    std::vector<Tag> b = {Tag("foo"), Tag("bar")};           
    std::list<Tag> c = {Tag("foo"), Tag("bar")};

    //std::cout << t.isIn(a) << std::endl;
    std::cout << t.isIn(b) << std::endl;
    std::cout << t.isIn(c) << std::endl;
}