C++ 为什么在C++;标准图书馆?
当想要进行连续复制时,出现了一个用例(1.使用C++ 为什么在C++;标准图书馆?,c++,c++-standard-library,stl-algorithm,C++,C++ Standard Library,Stl Algorithm,当想要进行连续复制时,出现了一个用例(1.使用copy\u if)但是从一个值容器到一个指向这些值的指针容器(2.使用transform)是可行的 有了可用的工具,我无法在以下两个步骤中完成: #include <vector> #include <algorithm> using namespace std; struct ha { int i; explicit ha(int a) : i(a) {} }; int main() {
copy\u if
)但是从一个值容器到一个指向这些值的指针容器(2.使用transform
)是可行的
有了可用的工具,我无法在以下两个步骤中完成:
#include <vector>
#include <algorithm>
using namespace std;
struct ha {
int i;
explicit ha(int a) : i(a) {}
};
int main()
{
vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
// GOAL : make a vector of pointers to elements with i < 2
vector<ha*> ph; // target vector
vector<ha*> pv; // temporary vector
// 1.
transform(v.begin(), v.end(), back_inserter(pv),
[](ha &arg) { return &arg; });
// 2.
copy_if(pv.begin(), pv.end(), back_inserter(ph),
[](ha *parg) { return parg->i < 2; }); // 2.
return 0;
}
transform\u,有什么原因吗?现有工具的组合是否是一个足够的解决方案和/或被认为性能良好?
标准库支持基本算法 如果可能,容器和算法应该相互独立 类似地,可以由现有算法组成的算法也很少包括在内,例如速记 如果需要转换If,则可以编写它。如果您想要它/today/,由现成的made组成,并且不产生开销,您可以使用具有惰性范围的范围库,例如: 正如@hvd在一篇评论中指出的,
transform\u如果
double导致不同的类型(double
,在本例中)。构图顺序很重要,使用Boost Range,您还可以编写:
v | transformed(arg1 * arg1 / 7.0) | filtered(arg1 < 2.0)
本标准的设计应尽量减少重复 在这种特殊情况下,您可以通过一个简单的循环范围,以更可读、更简洁的方式实现算法的目标
// another way
vector<ha*> newVec;
for(auto& item : v) {
if (item.i < 2) {
newVec.push_back(&item);
}
}
//另一种方法
向量newVec;
用于(自动项目:v(&E){
如果(项目i<2){
newVec.push_back(&item);
}
}
我已经修改了这个示例,使其能够编译,添加了一些诊断,并同时介绍了OP的算法和我的算法
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
using namespace std;
struct ha {
explicit ha(int a) : i(a) {}
int i; // added this to solve compile error
};
// added diagnostic helpers
ostream& operator<<(ostream& os, const ha& t) {
os << "{ " << t.i << " }";
return os;
}
ostream& operator<<(ostream& os, const ha* t) {
os << "&" << *t;
return os;
}
int main()
{
vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
// GOAL : make a vector of pointers to elements with i < 2
vector<ha*> ph; // target vector
vector<ha*> pv; // temporary vector
// 1.
transform(v.begin(), v.end(), back_inserter(pv),
[](ha &arg) { return &arg; });
// 2.
copy_if(pv.begin(), pv.end(), back_inserter(ph),
[](ha *parg) { return parg->i < 2; }); // 2.
// output diagnostics
copy(begin(v), end(v), ostream_iterator<ha>(cout));
cout << endl;
copy(begin(ph), end(ph), ostream_iterator<ha*>(cout));
cout << endl;
// another way
vector<ha*> newVec;
for(auto& item : v) {
if (item.i < 2) {
newVec.push_back(&item);
}
}
// diagnostics
copy(begin(newVec), end(newVec), ostream_iterator<ha*>(cout));
cout << endl;
return 0;
}
#包括
#包括
#包括
#包括
使用名称空间std;
结构ha{
显式ha(inta):i(a){
int i;//添加此命令是为了解决编译错误
};
//添加了诊断帮助程序
ostream&operator新的for循环表示法在许多方面减少了对访问集合中每个元素的算法的需求,现在只需编写一个循环并将逻辑放在适当的位置就可以了
std::vector< decltype( op( begin(coll) ) > output;
for( auto const& elem : coll )
{
if( pred( elem ) )
{
output.push_back( op( elem ) );
}
}
std::vector输出;
用于(自动常量和元素:coll)
{
if(pred(elem))
{
输出。推回(op(elem));
}
}
现在,它真的为算法提供了很多价值吗?是的,该算法对C++03很有用,事实上我有一个算法,我们现在不需要它,所以添加它没有真正的优势
请注意,在实际使用中,您的代码也不会总是这样:您不一定有函数“op”和“pred”,可能需要创建lambda使其“适合”当逻辑复杂时,分离关注点是很好的,如果只是从输入类型中提取一个成员并检查其值或将其添加到集合中,则再次比使用算法简单得多
此外,一旦添加某种类型的转换_if,您必须决定是在转换之前还是之后应用谓词,或者甚至有两个谓词并在两个位置应用它
那么我们要做什么呢?添加3个算法?(如果编译器可以在转换的任一端应用谓词,用户很容易错误地选择错误的算法,代码仍然编译,但产生错误的结果)
此外,如果集合很大,用户是否希望使用迭代器或map/reduce循环?随着map/reduce的引入,等式中的复杂性会增加
从本质上说,库提供了工具,用户可以在这里使用这些工具来适应他们想要做的事情,而不是像通常的算法那样(参见上面的用户如何尝试使用累积来适应他们真正想要做的事情)
举一个简单的例子,一个map。对于每个元素,如果键是偶数,我将输出值
std::vector< std::string > valuesOfEvenKeys
( std::map< int, std::string > const& keyValues )
{
std::vector< std::string > res;
for( auto const& elem: keyValues )
{
if( elem.first % 2 == 0 )
{
res.push_back( elem.second );
}
}
return res;
}
std::vectorvaluesOfEvenKeys
(标准::映射常量和键值)
{
std::vectorres;
用于(自动常量和元素:关键值)
{
如果(元素首%2==0)
{
res.推回(1秒);
}
}
返回res;
}
很好,很简单。想把它应用到转换if算法中吗?很抱歉这么长时间后再次提出这个问题。我最近有一个类似的要求。我通过编写一个版本的back\u insert\u迭代器解决了这个问题,该迭代器需要一个boost::可选:
template<class Container>
struct optional_back_insert_iterator
: public std::iterator< std::output_iterator_tag,
void, void, void, void >
{
explicit optional_back_insert_iterator( Container& c )
: container(std::addressof(c))
{}
using value_type = typename Container::value_type;
optional_back_insert_iterator<Container>&
operator=( const boost::optional<value_type> opt )
{
if (opt) {
container->push_back(std::move(opt.value()));
}
return *this;
}
optional_back_insert_iterator<Container>&
operator*() {
return *this;
}
optional_back_insert_iterator<Container>&
operator++() {
return *this;
}
optional_back_insert_iterator<Container>&
operator++(int) {
return *this;
}
protected:
Container* container;
};
template<class Container>
optional_back_insert_iterator<Container> optional_back_inserter(Container& container)
{
return optional_back_insert_iterator<Container>(container);
}
模板
结构可选返回插入迭代器
:public std::iterator
{
显式可选返回插入迭代器(容器和c)
:容器(标准::地址(c))
{}
使用value\u type=typename容器::value\u type;
可选的返回插入迭代器&
运算符=(常量boost::可选选项)
{
如果(选择){
容器->推回(std::move(opt.value());
}
归还*这个;
}
可选的返回插入迭代器&
运算符*(){
归还*这个;
}
可选的返回插入迭代器&
运算符++(){
归还*这个;
}
可选的返回插入迭代器&
运算符++(int){
归还*这个;
}
受保护的:
集装箱*集装箱;
};
模板
可选返回插入迭代器可选返回插入器(容器和容器)
{
返回可选的返回插入迭代器(容器);
}
这样使用:
transform(begin(s), end(s),
optional_back_inserter(d),
[](const auto& s) -> boost::optional<size_t> {
if (s.length() > 1)
return { s.length() * 2 };
else
return { boost::none };
});
变换(开始、结束),
可选的后置插入器(d),
[](const auto&s)->boost::可选{
如果(s.长度()>1)
返回{s.length()*2};
其他的
返回{boost::none};
});
您可以使用复制(如果
随附为什么不可以?定义输出
(请参阅)
std::vector< std::string > valuesOfEvenKeys
( std::map< int, std::string > const& keyValues )
{
std::vector< std::string > res;
for( auto const& elem: keyValues )
{
if( elem.first % 2 == 0 )
{
res.push_back( elem.second );
}
}
return res;
}
template<class Container>
struct optional_back_insert_iterator
: public std::iterator< std::output_iterator_tag,
void, void, void, void >
{
explicit optional_back_insert_iterator( Container& c )
: container(std::addressof(c))
{}
using value_type = typename Container::value_type;
optional_back_insert_iterator<Container>&
operator=( const boost::optional<value_type> opt )
{
if (opt) {
container->push_back(std::move(opt.value()));
}
return *this;
}
optional_back_insert_iterator<Container>&
operator*() {
return *this;
}
optional_back_insert_iterator<Container>&
operator++() {
return *this;
}
optional_back_insert_iterator<Container>&
operator++(int) {
return *this;
}
protected:
Container* container;
};
template<class Container>
optional_back_insert_iterator<Container> optional_back_inserter(Container& container)
{
return optional_back_insert_iterator<Container>(container);
}
transform(begin(s), end(s),
optional_back_inserter(d),
[](const auto& s) -> boost::optional<size_t> {
if (s.length() > 1)
return { s.length() * 2 };
else
return { boost::none };
});
template <class InputIt, class OutputIt, class BinaryOp>
OutputIt
transform_if(InputIt it, InputIt end, OutputIt oit, BinaryOp op)
{
for(; it != end; ++it, (void) ++oit)
op(oit, *it);
return oit;
}
std::vector a{1, 2, 3, 4};
std::vector b;
return transform_if(a.begin(), a.end(), b.begin(),
[](auto oit, auto item) // Note the use of 'auto' to make life easier
{
if(CONDITION(item)) // Here's the 'if' part
*oit++ = TRANSFORM(item); // Here's the 'transform' part
}
);
struct my_inserter: back_insert_iterator<vector<ha *>>
{
my_inserter(vector<ha *> &dst)
: back_insert_iterator<vector<ha *>>(back_inserter<vector<ha *>>(dst))
{
}
my_inserter &operator *()
{
return *this;
}
my_inserter &operator =(ha &arg)
{
*static_cast< back_insert_iterator<vector<ha *>> &>(*this) = &arg;
return *this;
}
};
int main()
{
vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
// GOAL : make a vector of pointers to elements with i < 2
vector<ha*> ph; // target vector
my_inserter yes(ph);
copy_if(v.begin(), v.end(), yes,
[](const ha &parg) { return parg.i < 2; });
return 0;
}
#include <algorithm>
#include <functional> // std::reference_wrapper
#include <iostream>
#include <vector>
struct ha {
int i;
};
int main() {
std::vector<ha> v { {1}, {7}, {1}, };
std::vector<std::reference_wrapper<ha const> > ph; // target vector
copy_if(v.begin(), v.end(), back_inserter(ph), [](const ha &parg) { return parg.i < 2; });
for (ha const& el : ph)
std::cout << el.i << " ";
}
1 1
#include <iostream>
#include <optional>
#include <vector>
template <
class InputIterator, class OutputIterator,
class UnaryOperator
>
OutputIterator filter_transform(InputIterator first1, InputIterator last1,
OutputIterator result, UnaryOperator op)
{
while (first1 != last1)
{
if (auto mapped = op(*first1)) {
*result = std::move(mapped.value());
++result;
}
++first1;
}
return result;
}
struct ha {
int i;
explicit ha(int a) : i(a) {}
};
int main()
{
std::vector<ha> v{ ha{1}, ha{7}, ha{1} }; // initial vector
// GOAL : make a vector of pointers to elements with i < 2
std::vector<ha*> ph; // target vector
filter_transform(v.begin(), v.end(), back_inserter(ph),
[](ha &arg) { return arg.i < 2 ? std::make_optional(&arg) : std::nullopt; });
for (auto p : ph)
std::cout << p->i << std::endl;
return 0;
}
1 1