【问题标题】:Function returning a container containing specific elements of input container返回包含输入容器的特定元素的容器的函数
【发布时间】:2018-08-07 13:54:16
【问题描述】:

我有一个vectorlist,我只想将代码应用于特定元素。例如

class Container : public std::vector<Element*>

或者

class Container : public std::list<Element*>

还有:

Container newContainer = inputContainer.Get(IsSomething);
if (!newContainer.empty()) {
    for (Element* const el: newContainer ) {
        [some stuff]
    }
} else {
    for (Element* const el : inputContainer) {
        [some stuff]
    }
}

我写了一个成员函数Get()如下。

template<typename Fn>
auto Container::Get(const Fn& fn) const {
    Container output;
    std::copy_if(cbegin(), cend(), std::inserter(output, output.end()), fn);
    return output;
}

IsSomething 将是一个 lambda,例如

auto IsSomething= [](Element const* const el)->bool { return el->someBool; };

从性能的角度来看:这是一个好方法吗?还是复制删除比较好?

template<typename Fn>
auto Container::Get(const Fn& fn) const {
    Container output(*this);
    output.erase(std::remove_if(output.begin(), output.end(), fn), end(output));
    return output;
}

或者有更好的方法吗?

编辑:不同的例子

由于我之前的示例可以用更好的方式编写,让我们展示一个不同的示例:

while (!(container2 = container1.Get(IsSomething)).empty()&&TimesFooCalled<SomeValue)
{
    Container container3(container2.Get(IsSomething));
    if (!container3.empty()) {
        Foo(*container3.BestElement());
    } else {
        Foo(*container2.BestElement());
    }
}

【问题讨论】:

  • 你想要back_inserteroutput.end() 将在插入第一个元素后立即失效。
  • 如果你想要性能我建议你看看this
  • 是的。它适用于任何提供push_back 方法的东西。 std::vectorstd::list 都可以。
  • 子类化容器是个好主意吗?我认为这不是一个好主意stackoverflow.com/questions/6806173/…
  • @JHBonarius 请看我的扩展答案。

标签: c++ stl copy


【解决方案1】:

不回答您的直接问题,但请注意,您可以在不复制任何内容的情况下实现原始算法。像这样的:

bool found = false;
for (Element* const el: inputContainer) {
  if (IsSomething(el)) {
    found = true;
    [some stuff]
  }
}
if (!found) {
  for (Element* const el : inputContainer) {
    [some stuff]
  }
}

【讨论】:

    【解决方案2】:

    我使用的通常模式是这样的:

    for(auto const * item : inputContainer) if(IsSomething(item)) {
        // Do stuff with item
    }
    

    这通常足够好,所以其他方法似乎有点过头了。

    为了获得更好的性能,最好不要从您获得的列表中复制或删除元素。以我的经验,如果你只浏览一次列表,它会更快,因为缓存的原因。所以这是我要从列表中找到一个或另一个“最佳”值的方法:

    auto const isBetter = std::greater<Element>();
    Element const * best = nullptr, const * alt_best = nullptr;
    
    for(Element const * current : inputContainer) {
        if(IsSomething(current)) {
            if(!best || isBetter(*best, *current)) best = current;
        } else {
            if(!alt_best || isBetter(*alt_best, *current)) alt_best = current;
        }
    }
    
    if(best) {
        // do something with best
    } else if(alt_best) {
        // do something with alt_best
    } else {
        // empty list
    }
    

    如果您发现自己经常这样做,或者您想将此作为类界面的一部分,您可以考虑编写一个迭代器来跳过您不喜欢的元素。

    如果您真的想从列表中删除该项目,您可以执行以下操作:

    inputContainer.erase(std::remove_if(std::begin(inputContainer), std::end(inputContainer), 
        [](Element const *item) {
            if(IsSomething(item)) {
                // Do something with item
                return true;
            }
            return false;
        }
    ));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多