copy_if具有不同类型

ary*_*naq 11 c++ c++14

如果我知道如何提取匹配类型,是否有一种现代方式表达从不同类型的源容器有条件地复制到目标容器的意图?

将问题作为代码示例提出更容易:

#include <algorithm>
#include <vector>

struct Foo {};
struct FooBar{
    bool is_valid;
    Foo foo;
};

std::vector<Foo> get_valid_foos(const std::vector<FooBar>& foobars){
    std::vector<Foo> valid_foos;
    for(const auto& fbar : foobars){
        if(fbar.is_valid)
            valid_foos.push_back(fbar.foo);
    }
    return valid_foos;
}

std::vector<Foo> get_valid_foos_modern(const std::vector<FooBar>& foobars){
    std::vector<Foo> valid_foos;
    std::copy_if(foobars.begin(), foobars.end(), std::back_inserter(valid_foos),
        [](const auto& foobar){
            return foobar.is_valid;
        });
    //?? std::copy requires input and output types to match
    return valid_foos;
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/g/miPbfW

Bar*_*rry 11

使用range-v3:

std::vector<Foo> get_valid_foos(const std::vector<FooBar>& foobars) {
    return foobars
        | view::filter(&FooBar::is_valid)
        | view::transform(&FooBar::foo);
}
Run Code Online (Sandbox Code Playgroud)

这很有表现力.

  • @arynaq很可能很多Ranges TS将使用C++ 20. (3认同)

Xir*_*ema 5

与提出的其他答案一样,范围为这个问题提供了一个非常简洁的解决方案。我们距离 C++20 标准化还有几年时间(并且在它在企业环境中变得可用之前还需要几年时间),所以我们需要一个与 C++17 兼容的解决方案。

您正在寻找的是一个假设transform_if,由于各种原因未包含在标准库中

你有几个选择。

最简单的就是结合std::copy_ifand std::transform

std::vector<Foo> get_valid_foos_modern(const std::vector<FooBar>& foobars){
    std::vector<FooBar> valid_foobars;
    std::copy_if(foobars.begin(), foobars.end(), std::back_inserter(valid_foobars), [](const auto& foobar){
        return foobar.is_valid;
    });
    std::vector<Foo> valid_foos;
    std::transform(valid_foobars.begin(), valid_foobars.end(), std::back_inserter(valid_foos), [](auto const& fooBar) {return fooBar.foo;});
    return valid_foos;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的缺点是它FooBar为每个要转换的对象创建临时对象,您可能会发现这是不合需要的。您可以推出自己的transform_if算法实现:

template<typename InputIterator, typename OutputIterator, typename Predicate, typename TransformFunc>
OutputIterator transform_if(
    InputIterator&& begin, 
    InputIterator&& end, 
    OutputIterator&& out, 
    Predicate&& predicate, 
    TransformFunc&& transformer
) {
    for(; begin != end; ++begin, ++out) {
        if(predicate(*begin))
            *out = transformer(*begin);
    }
    return out;
}
Run Code Online (Sandbox Code Playgroud)

然后你就可以直接在你的代码中使用它:

std::vector<Foo> get_valid_foos_modern(const std::vector<FooBar>& foobars){
    std::vector<Foo> valid_foos;
    transform_if(
        foobars.begin(), 
        foobars.end(), 
        std::back_inserter(valid_foos), 
        [](const auto& foobar) { return foobar.is_valid;},
        [](auto const& foobar) { return foobar.foo;}
    );
    return valid_foos;
}
Run Code Online (Sandbox Code Playgroud)