应用函数后查找重复值的函数

时间:2018-06-02 22:29:23

标签: c++ c++11

我想在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*的“原始”指针的使用,但这对于一个单独的问题可能更好)

4 个答案:

答案 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::optionalboost::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);
         });
     });
}