C++ 如果使用不同类型,请复制\u

C++ 如果使用不同类型,请复制\u,c++,c++14,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<FooB

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

以代码示例的形式提出问题更容易:

#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;
}
#包括

使用:

std::vector get\u valid\u foos(const std::vector&foobars){
返回foobars
|视图::过滤器(&FooBar::是否有效)
|视图::转换(&FooBar::foo);
}

这很有表现力

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

您要查找的是一个假设的
转换\u if
,它未包含在

你有两个选择

最简单的方法是将
std::copy_if
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;
}
std::vector<FooBar> foobars_with_valid_foos;
std::copy_if(
    foobars.begin()
,   foobars.end()
,   std::back_inserter(foobars_with_valid_foos)
,   [](const auto& foobar){
        return foobar.is_valid;
    }
);
std::vector<Foo> valid_foos;
std::transform(
    foobars_with_valid_foos.begin()
,   foobars_with_valid_foos.end()
,   std::back_inserter(valid_foos)
,   [](const auto& foobar){
        return foobar.foo;
    }
);
return valid_foos;
然后您可以在代码中直接使用:

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;
}
std::vector get\u valid\u foos\u modern(const std::vector&foobars){
std::向量有效的\u foos;
变换(
foobars.begin(),
foobars.end(),
标准::背面插入器(有效),
[](const auto&foobar){return foobar.is_valid;},
[](auto const&foobar){return foobar.foo;}
);
返回有效的\u foos;
}

这将调用一个假设的
std::transform\u if
,它不可用()

一个有点昂贵的解决方法是将
std::copy\u if
复制到一个临时向量中,然后进行
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;
}
std::vector<FooBar> foobars_with_valid_foos;
std::copy_if(
    foobars.begin()
,   foobars.end()
,   std::back_inserter(foobars_with_valid_foos)
,   [](const auto& foobar){
        return foobar.is_valid;
    }
);
std::vector<Foo> valid_foos;
std::transform(
    foobars_with_valid_foos.begin()
,   foobars_with_valid_foos.end()
,   std::back_inserter(valid_foos)
,   [](const auto& foobar){
        return foobar.foo;
    }
);
return valid_foos;
std::向量foobars\u与有效foos;
复制(
foobars.begin()
,foobars.end()
,std::back\u插入器(foobars\u带有有效的foos)
,[](const auto和foobar){
返回foobar.u是否有效;
}
);
std::向量有效的\u foos;
std::transform(
foobars\u与\u有效\u foos.begin()
,foobars\u与\u valid\u foos.end()
,std::back\u inserter(有效的\u foos)
,[](const auto和foobar){
返回foobar.foo;
}
);
返回有效的\u foos;

虽然不如range-v3好,但您可以使用:

std::vector get\u valid\u foos(const std::vector&foobars){
std::向量结果;
推回(
结果,FooBar | boost::adapters::filtered([](const FooBar&FooBar){
返回foobar.u是否有效;
})| boost::adapters::transformed([](常量FooBar和FooBar){
返回foobar.foo;
}));
返回结果;
}

返回插入器或迭代器
它将尝试并
推回
分配给
它的任何内容。
当前,由于
it=foobar
格式不正确,您会收到一个错误。事实上,foo.push\u back(foobar)
的向量本身是病态的

如果有办法将
FooBar
隐式转换为
Foo
。。。等待有!嗯,令人恼火的是,它在
Foo
FooBar
之间引入了循环依赖关系。让我们用CRTP打破它

template<class TFoo>
struct TFooBar
{
    bool is_valid;
    TFoo foo;
};
struct Foo
{
    Foo() = default;
    Foo(TFooBar<Foo> const& src) { *this = src.foo; }
};
using FooBar = TFooBar<Foo>;
演示:

#包括
#包括
#包括
#包括
模板
类映射插入器
:public std::back\u insert\u迭代器
{
受保护的:
使用Out=typename容器::值\类型;
使用Transformer=std::function;
公众:
MappedInsertIterator()=删除;
模板
需要std::是否可发票
显式映射插入器(容器&c、F和&fn);
virtual~mappedinseriterator()=默认值;
公众:
自动运算符*()->MappedInsertator&;
自动运算符=(常量自动和值)->MappedInsertator&;
受保护的:
变压器m_fn;
};
模板
模板
需要std::是否可发票
内联MappedInsertIterator::MappedInsertIterator(容器&c、F&&fn)
:std::back\u insert\u迭代器(c)
,m_fn(标准:正向(fn))
{}
模板
内联自动MappedInsertIterator::运算符*()->MappedInsertIterator&
{return*this;}
模板
自动MappedInsertIterator::operator=(常量自动和值)->MappedInsertIterator&
{
std::back_insert_iterator::operator=(m_fn(value));
归还*这个;
}
int main()
{
结构遥测{浮动电压;无符号时间戳;};
向量项=
{
遥测{.voltage=200,.timestamp=101},//已接受
遥测{.voltage=250,.timestamp=102},//已接受
遥测{.voltage=300,.timestamp=203},//被拒绝
};
静态自动谓词=[](常量遥测&t){返回t.timestamp<200;};
静态自动转换=[](常量遥测&t){返回t.voltage;};
矢量电压;
使用迭代器\u t=mappedinseriterator;
std::copy_if(items.cbegin()、items.cend()、迭代器(电压、变换)、谓词);
用于(常数自动和电压:电压)

std::cout Related:。如果
,则没有
std::transform\u,但是如果您愿意使用库,则有几个库可以做到这一点。当然,
std::copy\u如果
可以将源类型分配给目标类型,则工作正常。@PeteBecker:附带条件是,如果分配给出警告,您将得到一个临时值警告消息中的后期实例化堆栈。VS2017为int-to-float转换生成大约50行代码。“提取函数”可以像
static\u cast()一样简单
@MSalters——是的,有些编译器很难使用。特别是当他们认为他们比你更了解你的需求时。关闭愚蠢的警告!这很漂亮,为什么不在标准中?@arynaq正在添加类似的内容。从我在文档中看到的内容来看,很遗憾没有MSVC支持。@如果使用arynaq,则支持范围有限的版本V3@arynaq在C++20中可能会有很多范围的TS。我以前从未见过这种格式样式……我从未见过
template<class TFoo>
struct TFooBar
{
    bool is_valid;
    TFoo foo;
};
struct Foo
{
    Foo() = default;
    Foo(TFooBar<Foo> const& src) { *this = src.foo; }
};
using FooBar = TFooBar<Foo>;
auto get_valid_foos_modern(const std::vector<FooBar>& foobars){
    std::vector<Foo> result;
    std::copy_if(begin(foobars), end(foobars), std::back_inserter(result),
        [](const auto& foobar) {
            return foobar.is_valid;
    });
    return result;
}
#include <iterator>
#include <functional>
#include <vector>
#include <iostream>

template<typename Container,
         typename In>
class MappedInsertIterator
    : public std::back_insert_iterator<Container>
{
protected:
    using Out = typename Container::value_type;
    using Transformer = std::function<Out(const In&)>;

public:
    MappedInsertIterator() = delete;

    template<typename F>
        requires std::is_invocable_r_v<Out, F, In>
    explicit MappedInsertIterator(Container& c, F&& fn);

    virtual ~MappedInsertIterator() = default;

public:
    auto operator*() -> MappedInsertIterator&;
    auto operator=(const auto& value) -> MappedInsertIterator&;

protected:
    Transformer m_fn;
};

template<typename Container, typename In>
template<typename F>
    requires std::is_invocable_r_v<typename Container::value_type, F, In>
inline MappedInsertIterator<Container, In>::MappedInsertIterator(Container& c, F&& fn)
    : std::back_insert_iterator<Container>(c)
    , m_fn(std::forward<F>(fn))
{}

template<typename Container, typename In>
inline auto MappedInsertIterator<Container, In>::operator*() -> MappedInsertIterator&
{ return *this; }

template<typename Container, typename In>
auto MappedInsertIterator<Container, In>::operator=(const auto& value) -> MappedInsertIterator&
{
    std::back_insert_iterator<Container>::operator=(m_fn(value));
    return *this;
}


int main()
{
    struct Telemetry { float voltage; unsigned timestamp; };

    std::vector<Telemetry> items =
        {
            Telemetry { .voltage = 200, .timestamp = 101 }, // accepted
            Telemetry { .voltage = 250, .timestamp = 102 }, // accepted
            Telemetry { .voltage = 300, .timestamp = 203 }, // rejected
        };

    static auto predicate = [](const Telemetry& t){ return t.timestamp < 200; };
    static auto transform = [](const Telemetry& t){ return t.voltage; };

    std::vector<float> voltages;
    using iterator_t = MappedInsertIterator<decltype(voltages), Telemetry>;
    std::copy_if(items.cbegin(), items.cend(), iterator_t(voltages, transform), predicate);

    for (const auto& v : voltages)
        std::cout << v << std::endl;
}