使用<algorithm>
中的模板函数,您可以执行此类操作
struct foo
{
int bar, baz;
};
struct bar_less
{
// compare foo with foo
bool operator()(const foo& lh, const foo& rh) const
{
return lh.bar < rh.bar;
}
template<typename T> // compare some T with foo
bool operator()(T lh, const foo& rh) const
{
return lh < rh.bar;
}
template<typename T> // compare foo with some T
bool operator()(const foo& lh, T rh) const
{
return lh.bar < rh;
}
};
int main()
{
foo foos[] = { {1, 2}, {2, 3}, {4, 5} };
bar_less cmp;
int bar_value = 2;
// find element {2, 3} using an int
auto it = std::lower_bound(begin(foos), end(foos), bar_value, cmp);
std::cout << it->baz;
}
在像std::set
这样的find
方法中,您必须传递set::key_type
类型的对象,这通常会强制您创建虚拟对象。
set<foo> foos;
foo search_dummy = {2,3}; // don't need a full foo object;
auto it = foos.find(search_dummy);
如果只能拨打foos.find(2)
,那将会非常有用。有没有理由find
不能成为模板,接受可以传递给较少谓词的所有内容。如果它只是缺失,为什么不在C ++ 11中(我认为它不是)。
主要问题是为什么不可能,如果它是可行的,为什么决定不提供它的标准。第二个问题,你可以建议解决方法:-)(boost::multi_index_container
刚刚过了我的脑海,它提供了从值类型的关键提取)
构造值类型更昂贵的另一个示例。键name
是该类型的一部分,不应在map键中用作副本;
struct Person
{
std::string name;
std::string adress;
std::string phone, email, fax, stackoferflowNickname;
int age;
std::vector<Person*> friends;
std::vector<Relation> relations;
};
struct PersonOrder
{
// assume that the full name is an unique identifier
bool operator()(const Person& lh, const Person& rh) const
{
return lh.name < rh.name;
}
};
class PersonRepository
{
public:
const Person& FindPerson(const std::string& name) const
{
Person searchDummy; // ouch
searchDummy.name = name;
return FindPerson(searchDummy);
}
const Person& FindPerson(const Person& person) const;
private:
std::set<Person, PersonOrder> persons_;
// what i want to avoid
// std::map<std::string, Person> persons_;
// Person searchDummyForReuseButNotThreadSafe;
};
答案 0 :(得分:3)
std::find_if
适用于未排序的范围。所以你可以传递你想要的任何谓词。
std::set<T>
始终使用Comparator
模板参数(默认情况下为std::less<T>
)来维护集合的顺序,以及再次查找元素。
因此,如果std::set::find
被模板化,则必须要求您只传递一个观察比较器总排序的谓词。
然后,std::lower_bound
和所有其他适用于已排序范围的算法已经完全需要,因此这不是一个新的或令人惊讶的要求。
所以,我想这只是一个疏忽,find_if()
上没有std::set
(比如说)。建议用于C ++ 17 :)(编辑::EASTL already has this,他们使用了比我更好的名字:find_as
)。
那就是说,你知道you shouldn't use std::set
,是吗?在大多数情况下,排序后的矢量会更快,并且您可以获得std::set
中缺少的灵活性。
编辑:正如Nicol所指出的那样,Boost和Loki(以及其他地方,我确定)已经实现了这个概念,但是看到了因为你无法使用他们的主要优势(内置find()
方法),所以使用裸std::vector
你不会失去太多。
答案 1 :(得分:2)
标准规定std::set::find
具有对数时间复杂度。在实践中,这是通过将std::set
实现为二叉搜索树来实现的,其中strict weak ordering比较用作排序标准。任何不满足相同严格弱排序标准的查找都不会满足对数复杂度。因此,这是一个设计决策:如果您想要对数复杂度,请使用std::set::find
。如果您可以交换复杂性以获得灵活性,请在集合上使用std::find_if。
答案 2 :(得分:1)
他们提供了你想要的东西,但却与你正在考虑的方式截然不同。
实际上,有两种不同的方式:一种是为包含的类构建一个构造函数,1)可以隐式使用,2)只需要您真正需要进行比较的元素子集。有了这个,您可以搜索foods.find(2);
。你将最终从2
创建一个临时对象,然后找到那个临时对象但它将是一个真正的临时对象。您的代码不必明确(任何地方)处理它。
编辑:我在这里谈论的是创建一个与你在地图中存储相同类型的实例,但是(可能)将你没有使用的任何一个字段作为“密钥”未初始化(或初始化为“不存在”的东西)。例如:
struct X {
int a; // will be treated as the key
std:::string data;
std::vector<int> more_data;
public:
X(int a) : a(a) {} // the "key-only" ctor
X(int a, std::string const &d, std::vector<int> const &m); // the normal ctor
};
std::set<X> s;
if (s.find(2)) { // will use X::X(int) to construct an `X`
// we've found what we were looking for
}
是的,当你用单参数构造函数构造你的X
(或者我称之为X的东西)时,很可能你构造的东西除了搜索之外什么都不可用。
结束编辑]
第二个,库为其提供更直接的支持通常更简单:如果您实际上只使用某些元素子集(可能只有一个)进行搜索,那么您可以创建std::map
代替std::set
。使用std::map
,显式/直接支持搜索您指定为密钥类型的任何实例。
答案 3 :(得分:0)
key_type是在set容器中定义的成员类型,作为Key的别名,它是第一个模板参数和容器中存储的元素的类型。
请参阅documentation。
对于用户定义的类型,库无法知道密钥类型是什么。碰巧的是,对于您的特定用例,密钥类型为int
。如果您使用set< int > s;
,则可以致电s.find( 2 );
。但是,如果要搜索set< foo >
并且只想传入一个整数,则需要帮助编译器(考虑set
的排序如何在foo
之间工作int
)。
答案 4 :(得分:0)
因为如果你想做std::find(2)
,除了两个int
之间的比较之外,你还必须定义foo
与foo
的比较方式。但由于int
和foo
是不同的类型,因此您实际上需要另外两个函数:
bool operator<(int, foo);
bool operator<(foo, int);
(或其他一些逻辑等效对)。
现在,如果你做那么,你实际上是在int
和foo
之间定义了一个双射函数,你也可以简单地使用std::map<int, foo>
并完成。
如果你仍然不想要std::map
,但是你想要排序搜索的好处,那么虚拟对象实际上是最简单的方法。
当然,标准std::set
可以提供一个成员函数,你传递一个接收foo
的函数并返回-1,0,1,如果它小于,等于或大于搜索了一个...但这不是C ++方式。请注意,即使bsearch()
也需要两个相同类型的参数!