【问题标题】:How to work around the fact that std::function has no operator==?如何解决 std::function 没有运算符 == 的事实?
【发布时间】:2015-01-28 23:23:22
【问题描述】:

问题:如果不是因为它无法编译,以下代码将非常具有表达力和简洁性,即使不一定很快。

它无法编译,因为您无法将 std::function 实例与 operator==() 进行比较。而 std::find() 试图做到这一点。

当然,我可以采用完全不同的实现方式,但我固执己见,喜欢下面的代码,我正在寻找“尽可能接近”的东西。

谁能给我一个漂亮的重写下面的代码来做同样的事情?

#include <functional>
#include <vector>

typedef std::function<bool(int)> Tester_t;
typedef std::vector<Tester_t> TesterSet_t;

bool Test(TesterSet_t &candidates, int foo)
{
    TesterSet_t dropouts;
    for( auto& tester : candidates )
    {
        if(!tester(foo))    
        {
            droputs.push_back(tester);
        }
    }

    while(!dropouts.empty())
    {
        // The following line is not compiling because std::function has no operator==()
        TesterSet_t::iterator culprit = 
            std::find( candidates.begin(), candidates.end(), dropouts.back() );
        candidates.erase(culprit);
        dropouts.pop_back();
    }
    return !candidates.empty();
}

【问题讨论】:

  • 您能描述一下您要解决的潜在问题吗? std::function 没有 == 运算符的原因是因为以这种方式比较函数没有多大意义。你怎么可能确定两个函数是否相等?
  • 简短回答:您应该创建一个包含唯一键和std::function 的结构,并拥有这些结构的向量。实际使用std::function 作为搜索键没有多大意义。 See here
  • 我敲开了这段代码 sn-p 这样我就不必用更长的代码打扰任何人了- 你用一些输入来面对它,你只想让那些仍然“在游戏中”的候选人留在身边。正如你所说, std::function 没有意义;)
  • @MattMcNabb 如果这里没有人有更好的主意,我会这样做。

标签: c++ c++11


【解决方案1】:

正如其他人所说,您不需要为此比较std::functions。使用标准 C++ 工具,这可以通过两行有效地(具有线性复杂度)实现:

bool Test(TesterSet_t &candidates, int foo)
{
    candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
        [foo](Tester_t& f){ return !f(foo); }), candidates.end());
    return !candidates.empty();
}

【讨论】:

  • 没有比较包含的对象,如果谓词返回true则删除。
【解决方案2】:

这里不需要平等。随手抹去

for (auto it = candidates.begin(); it != candidates.end(); ) {
    if (! (*it)(foo) ) {
        it = candidates.erase(it);
    }
    else {
        ++it;
    }
}
return !candidates.empty();

即使为std::function 定义了operator==,这也将比问题中的版本更快。

【讨论】:

  • 在迭代时擦除是通向灾难的好方法。至少多年来一直如此。但也许 C++11 voodoo 现在让这个合法化了?
  • 在迭代中擦除一直没问题,只要你操作得当。
  • @user2225104 如果你做错了很多事情会导致灾难。只需学习正确的方法即可。
  • 一般来说,我认为在迭代时改变容器是不可取的。en.wikipedia.org/wiki/Erase-remove_idiom
  • 迭代时擦除没问题,这是erase返回迭代器的主要原因之一! (关联容器在 C++03 中没有,但在 C++11 中得到了fixed)。
【解决方案3】:

如果你不需要删除候选人,你可以写:

bool Test(TesterSet_t &candidates, int foo)
{
    return std::any_of(candidates.begin(), candidates.end(), [&foo](Tester_t &tester) {
        return tester(foo);
    });
}

更新

好的,你需要删除候选人

bool Test(TesterSet_t &candidates, int foo)
{
    candidates.erase(
        std::remove_if(candidates.begin(), candidates.end(), [&foo](Tester_t &tester) {
            return !tester(foo);
        }),
        candidates.end()
    );
    return !candidates.empty();
}

【讨论】:

  • 擦除是其中的一部分。而且,(剩余的)候选人需要尝试一下。您的代码在发现第一个返回 false 时返回。代码的目标实际上是精简剩余候选集。
【解决方案4】:

简单的答案是在这种情况下使用std::function&lt;...&gt;,而是使用类似std::function&lt;...&gt; 的东西,它确实定义了一个相等运算符。为function&lt;...&gt; 定义相等运算符的方法是在构造时检测实际函数对象是否实际上包含相等运算符,如果是,则使对象具有可比性。否则,您要么会产生错误,要么会认为拥有此特定函数对象类型的对象无法比较。

然而,直接的观察是,大多数函数对象是不可比较的!例如,lambda 函数是不可比较的,std::bind()std::mem_fn() 也不会产生可比较的函数对象。同样,std::bind()std::mem_fn() 可以有自定义实现。没有办法让 lambda 函数具有可比性除非有一个空捕获,在这种情况下它们可以变成函数指针并可以进行比较。

平等感知函数对象的实现有点太长,无法快速输入响应。但是,您可以在github 上查看我的实现,以了解可比较的bind()mem_fn()。有关std::function&lt;...&gt; 的相等可比版本的实现,请参见this answer。如果 lambda 函数具有相同的签名并且所有捕获的值都是相等可比的,那么可能希望它们也具有可比性。

说了这么多,如果你能避免这种需要,最好避免它。但是,我遇到了一些用例,尽管有限制,可比较的 std::function&lt;...&gt; 还是相当方便的(即,并非所有函数对象都将被覆盖)。

【讨论】:

  • 确实如此。这就是为什么长期以来我认为 auto/lambda/std::function 方法存在根本缺陷的原因。他们真的应该引入一种新的内在类型,并使函数值和函数引用成为一等公民。他们如何解决捕获上下文实现并不是真正的 C++ 用户问题。有了这些,像 C-- 这样的努力可能已经全部保存下来了。
  • 问题不在于处理捕获。问题是您可以捕获不相等可比的对象(或对对象的引用)。你会想要这些的 lambda 函数,例如,当不需要比较它们时。所以你最终会得到无与伦比的函数对象。如何处理它们(将其视为错误/将它们视为不相等)在一定程度上取决于上下文,我实际上更喜欢该选择不要被纳入标准(即,在这种情况下不定义相等)。但是,如果捕获的对象具有可比性,则 lambda 函数的相等性会很好。
猜你喜欢
  • 1970-01-01
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多