Скажем, у меня есть vector<string>
, содержащий "a" и "b" , я хочу скопировать себя 2 раза, так что теперь вектор содержит "a" , "b" , "a" , "b" , "a", "b"
Что лучше, чем использовать for
и push_back
?
Скажем, у меня есть vector<string>
, содержащий "a" и "b" , я хочу скопировать себя 2 раза, так что теперь вектор содержит "a" , "b" , "a" , "b" , "a", "b"
Что лучше, чем использовать for
и push_back
?
Моя первоначальная мысль:
myvec.reserve(myvec.size()*3); //reserve not only speeds things upt it also protects us ftom iterator invalidation
vector<string>::iterator it = myvec.end(); //we ant to add two times the origional onto the end so we save the end of the origional end
myvec.insert(myvec.end(), myvec.begin(), it);
myvec.insert(myvec.end(), myvec.begin(), it);
благодаря Emilio Garavaglia для того, чтобы сначала указать на проблемы с этим, см. здесь множество причин, почему это имеет проблемы: Предоставляет ли std::vector:: insert() недействительные итераторы, если вектор достаточно комната (созданная с помощью резерва)?
Вторая попытка:
std::size_t size = myvec.size();
myvec.resize(size*3); //resize must protects us from iterator invalidation
vector<string>::iterator it = myvec.begin()+size;
std::copy(myvec.begin(),it,it);
std::copy(myvec.begin(),it,it+size);
поскольку реализация не реализует std::string. Конструктор по умолчанию whos выделяет что-то в куче, это должно вызывать меньше кучи доступа и, следовательно, быть быстрее, чем другие примеры.
Еще одна минимизация доступа к куче - это скопировать вектор в другую, вставить его, а затем переместиться в оригиналы, я украл код Эмилио Гаравалья и укусил его:
{
vector<string> m = { "a", "b" };
auto n = m; // keep initial value safe
m.reserve(3 * m.size()); // preallocate, to avoid consecutive allocations
m.insert(m.end, n.begin(), n.end());
std::for_each(n.begin(),n.end(),[&n](std::string &in){n.emplace_back(std::move(in));});
}
vector<string> m = { "a", "b" };
auto n = m; // keep initial value safe
m.reserve(3 * m.size()); // preallocate, to avoid consecutive allocations
m.insert(m.end, n.begin(), n.end());
m.insert(m.end, n.begin(), n.end());
Моя первая мысль заключается в том, чтобы избежать проблем с недействительными итераторами.
{ // note: nested scope
vector<string> temp(vec); // 1st copy
temp.insert(temp.end(), vec.begin(), vec.end()); // 2nd copy
temp.insert(temp.end(), vec.begin(), vec.end()); // 3rd copy
temp.swap(vec); // swap contents before temp is destroyed
}
В обзоре я думаю, что ответы PorkyBrain и Emilio Garavaglia могут иметь больше смысла.
Вот простой способ:
template<typename T, typename A1, typename A2>
std::vector<T, A1> operator+( std::vector<T, A1> left, std::vector<T, A2> const& right ) {
left.insert( left.end(), right.begin(), right.end() );
return left;
}
int main() {
std::vector<string> m = { "a", "b" );
m = m + m + m;
}
но, как заметил @ChristianAmmer, переопределение operator+
на a std::vector
неоднозначно. И это было бы неправильно.
Итак, вы можете пойти и написать целую инфикс с именем синтаксис оператора и использовать магию С++ для встраивания ее в язык С++, чтобы избавиться от этой двусмысленности. Пример:
#include <utility>
template<typename Operation, short op>
struct InfixOp {
Operation* self() { return static_cast<Operation*>(this); }
Operation const* self() const { return static_cast<Operation const*>(this); }
};
template<typename first_type, typename Infix, short op>
struct PartialForm {
Infix const* infix;
first_type a;
template<typename T>
PartialForm(T&& first, Infix const* i):infix(i), a(std::forward<T>(first)) {}
};
#define OVERRIDE_OPERATORS(OP, CODE) \
template<\
typename Left,\
typename Operation\
>\
PartialForm<typename std::remove_reference<Left>::type, Operation, CODE> operator OP( Left&& left, InfixOp<Operation, CODE> const& op ) {\
return PartialForm<typename std::remove_reference<Left>::type, Operation, CODE>( std::forward<Left>(left), op.self() );\
}\
\
template<\
typename Right,\
typename First,\
typename Operation\
>\
auto operator OP( PartialForm<First, Operation, CODE>&& left, Right&& right )\
->decltype( (*left.infix)( std::move( left.a ), std::forward<Right>(right)) )\
{\
return (*left.infix)( std::move( left.a ), std::forward<Right>(right) );\
}
OVERRIDE_OPERATORS(+, '+')
OVERRIDE_OPERATORS(*, '*')
OVERRIDE_OPERATORS(%, '%')
OVERRIDE_OPERATORS(^, '^')
OVERRIDE_OPERATORS(/, '/')
OVERRIDE_OPERATORS(==, '=')
OVERRIDE_OPERATORS(<, '<')
OVERRIDE_OPERATORS(>, '>')
OVERRIDE_OPERATORS(&, '&')
OVERRIDE_OPERATORS(|, '|')
//OVERRIDE_OPERATORS(!=, '!=')
//OVERRIDE_OPERATORS(<=, '<=')
//OVERRIDE_OPERATORS(>=, '>=')
template<typename Functor, char... ops>
struct Infix:InfixOp<Infix<Functor, ops...>, ops>...
{
Functor f;
template<typename F>
explicit Infix(F&& f_in):f(std::forward<F>(f_in)) {}
Infix(Infix<Functor, ops...> const& o):f(o.f) {}
Infix(Infix<Functor, ops...>&& o):f(std::move(o.f)) {}
Infix(Infix<Functor, ops...>& o):f(o.f) {}
template<typename L, typename R>
auto operator()( L&& left, R&& right ) const
-> decltype( f(std::forward<L>(left), std::forward<R>(right)))
{
return f(std::forward<L>(left), std::forward<R>(right));
}
};
template<char... ops, typename Functor>
Infix<Functor, ops...> make_infix( Functor&& f )
{
return Infix<Functor, ops...>(std::forward<Functor>(f));
}
#include <vector>
struct append_vectors {
template<typename T>
std::vector<T> operator()(std::vector<T> left, std::vector<T>const& right) const {
left.insert(left.end(), right.begin(), right.end());
return std::move(left);
}
};
struct sum_elements {
template<typename T>
std::vector<T> operator()(std::vector<T> left, std::vector<T>const& right) const {
for(auto it = left.begin(), it2 = right.begin(); it != left.end() && it2 != right.end(); ++it, ++it2) {
*it = *it + *it2;
}
return left;
}
};
struct prod_elements {
template<typename T>
std::vector<T> operator()(std::vector<T> left, std::vector<T>const& right) const {
for(auto it = left.begin(), it2 = right.begin(); it != left.end() && it2 != right.end(); ++it, ++it2) {
*it = *it * *it2;
}
return left;
}
};
#include <iostream>
int main() {
auto append = make_infix<'+'>(append_vectors());
auto sum = make_infix<'+'>(sum_elements());
auto prod = make_infix<'*'>(prod_elements());
std::vector<int> a = {1,2,3};
a = a +append+ a +append+ a;
a = a +sum+ a;
a = a *prod* a;
std::cout << a.size() << "\n";
for (auto&& x:a) {
std::cout << x << ",";
}
std::cout << "\n";
}
который имеет преимущество ясности в том месте, где вы его используете (я имею в виду, a = a +append+ a
довольно ясно, что он намеревается делать), ценой немного сложно понять, как это работает, и немного многословный для такой простой задачи.
Но по крайней мере двусмысленность исчезла, что хорошо, правильно?