[英](C++) Template to check whether an object is in a vector/array/list/…?
是否可以在C ++(11)中為函數創建一個模板,以檢查對象是否包含在std::vector
, std::array
或std::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::vector
, std::array
, std::list
(也許對於std::set
)只寫一次(沒有typedef的)上層代碼?
我找不到所有這些類的基本類型,這是我的第一個主意。
選項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
自己,但也可以使用它。
您可以使用模板:
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
的容器。
這些容器之間沒有通用的基本類型。 這不是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文件是調用該函數模板的唯一位置從。
該解決方案的明顯問題是,您必須手動提供要支持的每個重載,這意味着將來它不是非常“可擴展的”,但是在現實生活中,可能沒有那么多容器您想要支持的。 如果您想獲得全部通用性,則實際上必須使用模板方法(除非您要對容器進行類型擦除……但這超出了我在這里願意解釋的范圍)。
您可以使用嵌套的可變參數模板來實現此目的。 這是一個方便的演示:請注意魔術部分, template <template <typename...> class V, typename E>
。 可變參數模板是必需的,因為vector
, list
&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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.