我想在C ++中使用一个函数,它在集合中找到一个与集合中另一个项具有相同function(key)
值的项。
e.g。
std::set<int> ints = {1, -1, 3};
// Finds an item in the set with the same absolute value as
// another item.
int* dupe = find_duplicates(ints, [](int x) { return std::abs(x); });
// Should print -1 or 1
if (dupe != 0) std::cout << "Found dupe: " << *dupe << std::endl;
但是,我甚至无法为此方法编写函数签名。
在Java中,它就像static Integer findDuplicates<T, U>(Iterable<T>, Function<T, U> func)
。
在C ++中,我得到了以下内容,但它没有编译:
template<template <typename T> Collection, typename U>
T* find_duplicates(
const Collection<T>& collection,
const std::function<U(T)>& func) { ... }
我得到的错误是error: 'T' does not name a type
。
任何指针? (我也有兴趣一种方法来绕过T*
的“原始”指针的使用,但这对于一个单独的问题可能更好)
答案 0 :(得分:1)
在您的示例中,T
无法识别且几乎无用。您应该先引入类型T
,然后然后使用它来指定Collection
,如下所示:
template<typename T, template <typename> typename Collection, typename U>
T* find_duplicates(const Collection<T>& collection,
const std::function<U(T)>& func)
{
// some logic here
}
另外:请注意您在typename
之前错过了一个Collection
,正如我先前在评论中指出的那样。以上示例针对该建议进行了调整。
答案 1 :(得分:0)
对于您的具体示例,此类内容应该有效,
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
int main() {
std::set<int> ints = {1, -1, 3};
auto dupe = std::find_if(ints.begin(),ints.end(),[&](const int& first){
return std::find_if(ints.begin(),ints.end(),[&](const int& second) {
// so a the same value isn't checked against itself..
if (&first == &second) return false;
return std::abs(first) == std::abs(second);
}) != ints.end();
} );
if (dupe != ints.end()) std::cout << "Found dupe: " << *dupe << std::endl;
}
https://docs.python.org/2/library/threading.html#timer-objects
答案 2 :(得分:0)
不要对你的函数参数类型如此挑剔。你真的不需要collection
成为一个类模板专门化,只是你可以迭代的东西(甚至可能是一个C风格的数组)。 func
只需要是可调用的,并且强制它成为std::function
对象的效率可能低于直接使用函数指针或lambda,因为类型擦除std::function
会添加然后解析
所以工作声明可能是
template <typename Collection, typename Func>
auto find_duplicates(Collection& collection, const Func& func)
-> typename std::iterator_traits<decltype(std::begin(collection))>::value_type*;
我愿意:
#include <utility>
#include <iterator>
#include <set>
// Calling with an rvalue collection would be bad news.
template <typename Collection, typename Func>
void find_duplicates(Collection&&, const Func&) = delete;
template <typename Collection, typename Func>
auto find_duplicates(Collection& collection, const Func& func)
-> typename std::iterator_traits<decltype(std::begin(collection))>::value_type*
{
auto iter = std::begin(collection);
using value_type = typename std::iterator_traits<decltype(iter)>::value_type;
auto ptr_compare = [&func](value_type* p, value_type* q)
{ return func(*p) < func(*q); };
std::set<value_type*, decltype(ptr_compare)> iter_set{ptr_compare};
for (; iter != std::end(collection); ++iter) {
auto insert_result = iter_set.insert(std::addressof(*iter));
if (!insert_result.second)
return *insert_result.first;
}
return nullptr;
}
有时原始指针确实是答案。它表示该值可能为null,并且指针与该对象没有所有权关系。在这种情况下,在通过修改或销毁容器使其无效之后不使用指针取决于调用者,但它具有通常的指针到容器元素的无效语义,这取决于容器的细节。
这不会与std::vector<bool>
或任何其他伪容器一起工作,其中*collection.begin()
有一个&#34;代理&#34;类型。如果你想支持那些并且可以使用C ++ 17 std::optional
或boost::optional
,你可以返回一个optional<value_type>
而不是一个原始指针,尽管这会生成一个返回的重复副本值。如果您不能使用它们或者副本是不合需要的,可以编写一个包含迭代器的模板类iter_or_null<InputIter>
,operator*
和operator->
只调用迭代器&#39;运算符,并满足NullablePointer
concept。
答案 3 :(得分:0)
您实际上对模板的限制过于严格。您希望模板采用任何定义了std :: begin和std :: end的容器,以及任何可以接受容器包含的实例的函数对象。在模板世界中,您只需接受任何类型,以您希望的方式使用它,如果调用者没有提供兼容类型的对象,则会出现编译错误。
此外,不需要使用std :: function - 当你可以将函数对象作为具有推导类型的参数接受时,它最终可能会分配内存。
使用C ++ 14,自动推导的返回类型和lambdas的自动参数允许您编写非常通用的代码。这是我的解决方案。它将迭代器返回到指向找到的第一个副本的容器中,如果没有找到重复,则返回std :: end(c)。
#include <algorithm>
#include <cmath>
#include <iterator>
#include <iostream>
#include <set>
template<typename C, typename F>
auto find_duplicates(const C & c, const F & func)
{
return std::find_if(std::begin(c), std::end(c), [&](const auto & first) {
return std::any_of(std::begin(c), std::end(c), [&](const auto & second) {
return &first != &second && func(first) == func(second);
});
});
}
int main()
{
std::set<int> s{1, 3, -1};
auto const iter = find_duplicates(s, [](int x) { return std::abs(x); });
if (iter != std::end(s))
{
std::cout << "Duplicate found: " << *iter << '\n';
}
}
编辑:我刚刚注意到你指定了C ++ 11标签。它在C ++ 11中只是稍微复杂一点:
template<typename C, typename F>
auto find_duplicates(const C & c, const F & func) -> decltype(std::begin(c))
{
using ContainedType = decltype(*std::begin(c));
return std::find_if(std::begin(c), std::end(c), [&](const ContainedType & first) {
return std::any_of(std::begin(c), std::end(c), [&](const ContainedType & second) {
return &first != &second && func(first) == func(second);
});
});
}