Какие функции оболочки стандартной библиотеки C++ вы используете?
Этот вопрос, заданный сегодня утром, заставил меня задуматься о том, какие функции, по вашему мнению, отсутствуют в стандартной библиотеке C++, и как вы решили заполнить пробелы с помощью функций-оболочек. Например, моя собственная служебная библиотека имеет эту функцию для векторного добавления:
template <class T>
std::vector<T> & operator += ( std::vector<T> & v1,
const std::vector <T> & v2 ) {
v1.insert( v1.end(), v2.begin(), v2.end() );
return v1;
}
и этот для очистки (более или менее) любого типа - особенно полезен для таких вещей, как std::stack:
template <class C>
void Clear( C & c ) {
c = C();
}
У меня есть еще несколько, но мне интересно, какие вы используете? Пожалуйста, ограничьте ответы на функции- обертки - т.е. не более пары строк кода.
26 ответов
Довольно часто я использовал вектор как набор элементов в произвольном порядке (и, разумеется, когда мне не нужны быстрые проверки "это элемент в наборе"). В этих случаях вызов erase() - пустая трата времени, поскольку он переупорядочивает элементы, а меня не волнует порядок. Вот когда функция O(1) ниже пригодится - просто переместите последний элемент в положение, которое вы хотите удалить:
template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
v[index] = v.back();
v.pop_back();
}
содержит (container, val) (довольно просто, но удобно).
template<typename C, typename T>
bool contains(const C& container, const T& val) {
return std::find(std::begin(container), std::end(container), val) != std::end(container);
}
remove_unstable (начало, конец, значение)
Более быстрая версия std:: remove за исключением того, что она не сохраняет порядок остальных объектов.
template <typename T>
T remove_unstable(T start, T stop, const typename T::value_type& val){
while(start != stop) {
if (*start == val) {
--stop;
::std::iter_swap(start, stop);
} else {
++start;
}
}
return stop;
}
(в случае вектора типов pod (int, float и т. д.) и почти все объекты удаляются, std:: remove может быть быстрее).
template < class T >
class temp_value {
public :
temp_value(T& var) : _var(var), _original(var) {}
~temp_value() { _var = _original; }
private :
T& _var;
T _original;
temp_value(const temp_value&);
temp_value& operator=(const temp_value&);
};
Хорошо, так как кажется, что это не так просто, как я думал, вот объяснение:
В своем конструкторе temp_value
хранит ссылку на переменную и копию исходного значения переменной. В своем деструкторе он восстанавливает указанную переменную в исходное значение. Таким образом, независимо от того, что вы сделали с переменной между созданием и разрушением, она будет сброшена, когда temp_value
объект выходит из области видимости.
Используйте это так:
void f(some_type& var)
{
temp_value<some_type> restorer(var); // remembers var's value
// change var as you like
g(var);
// upon destruction restorer will restore var to its original value
}
Вот еще один подход, который использует трюк с защитой области видимости:
namespace detail
{
// use scope-guard trick
class restorer_base
{
public:
// call to flag the value shouldn't
// be restored at destruction
void dismiss(void) const
{
mDismissed = true;
}
protected:
// creation
restorer_base(void) :
mDismissed(false)
{}
restorer_base(const restorer_base& pOther) :
mDismissed(pOther.is_dismissed())
{
// take "ownership"
pOther.dismiss();
}
~restorer_base(void) {} // non-virtual
// query
bool is_dismissed(void) const
{
return mDismissed;
}
private:
// not copy-assignable, copy-constructibility is ok
restorer_base& operator=(const restorer_base&);
mutable bool mDismissed;
};
// generic single-value restorer, could be made
// variadic to store and restore several variables
template <typename T>
class restorer_holder : public restorer_base
{
public:
restorer_holder(T& pX) :
mX(pX),
mValue(pX)
{}
~restorer_holder(void)
{
if (!is_dismissed())
mX = mValue;
}
private:
// not copy-assignable, copy-constructibility is ok
restorer_holder& operator=(const restorer_holder&);
T& mX;
T mValue;
};
}
// store references to generated holders
typedef const detail::restorer_base& restorer;
// generator (could also be made variadic)
template <typename T>
detail::restorer_holder<T> store(T& pX)
{
return detail::restorer_holder<T>(pX);
}
Это всего лишь немного больше кода, но позволяет более чистое использование:
#include <iostream>
template <typename T>
void print(const T& pX)
{
std::cout << pX << std::endl;
}
void foo(void)
{
double d = 10.0;
double e = 12.0;
print(d); print(e);
{
restorer f = store(d);
restorer g = store(e);
d = -5.0;
e = 3.1337;
print(d); print(e);
g.dismiss();
}
print(d); print(e);
}
int main(void)
{
foo();
int i = 5;
print(i);
{
restorer r = store(i);
i *= 123;
print(i);
}
print(i);
}
Это удаляет его способность использоваться в классе, все же.
Вот третий способ достижения того же эффекта (который не страдает от проблем с потенциально деструкторами):
Реализация:
//none -- it is built into the language
Использование:
#include <iostream>
template <typename T>
void print(const T& pX)
{
std::cout << pX << std::endl;
}
void foo(void)
{
double d = 10.0;
double e = 12.0;
print(d); print(e);
{
double f(d);
double g(e);
f = -5.0;
g = 3.1337;
print(f); print(g);
e = std::move(g);
}
print(d); print(e);
}
int main(void)
{
foo();
int i = 5;
print(i);
{
int r(i);
r *= 123;
print(r);
}
print(i);
}
Не совсем фантик, но скандально пропавший copy_if
, Отсюда
template<typename In, typename Out, typename Pred>
Out copy_if(In first, In last, Out res, Pred Pr)
{
while (first != last) {
if (Pr(*first)) {
*res++ = *first;
}
++first;
}
return res;
}
template< typename T, std::size_t sz >
inline T* begin(T (&array)[sz]) {return array;}
template< typename T, std::size_t sz >
inline T* end (T (&array)[sz]) {return array + sz;}
Иногда я чувствую, что я в begin()
а также end()
ад. Я хотел бы иметь некоторые функции, такие как:
template<typename T>
void sort(T& x)
{
std::sort(x.begin(), x.end());
}
и другие подобные для std::find
, std::for_each
и в основном все алгоритмы STL.
я так чувствую sort(x)
гораздо быстрее читать / понимать, чем sort(x.begin(), x.end())
,
У меня есть заголовок, который помещает следующее в пространство имен "util":
// does a string contain another string
inline bool contains(const std::string &s1, const std::string &s2) {
return s1.find(s2) != std::string::npos;
}
// remove trailing whitespace
inline std::string &rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
// remove leading whitespace
inline std::string <rim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
// remove whitespace from both ends
inline std::string &trim(std::string &s) {
return ltrim(rtrim(s));
}
// split a string based on a delimeter and return the result (you pass an existing vector for the results)
inline std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
// same as above, but returns a vector for you
inline std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
return split(s, delim, elems);
}
// does a string end with another string
inline bool endswith(const std::string &s, const std::string &ending) {
return ending.length() <= s.length() && s.substr(s.length() - ending.length()) == ending;
}
// does a string begin with another string
inline bool beginswith(const std::string &s, const std::string &start) {
return s.compare(0, start.length(), start) == 0;
}
Функция полезности в любом наборе инструментов, конечно, copy_if
, Не совсем обертка, хотя.
Другой помощник, которого я обычно использую, deleter
функтор, с которым я пользуюсь std::for_each
удалить все указатели в контейнере.
[править] Копаясь в моем "sth.h" я также нашел vector<wstring> StringSplit(wstring const&, wchar_t);
Я больше этим не пользуюсь, но раньше он был основным:
template<typename T>
std::string make_string(const T& data) {
std::ostringstream stream;
stream << data;
return stream.str();
}
Буду обновлять больше, как я их помню.:П
Скандально пропавший erase
алгоритм:
template <
class Container,
class Value
>
void erase(Container& ioContainer, Value const& iValue)
{
ioContainer.erase(
std::remove(ioContainer.begin(),
ioContainer.end(),
iValue),
ioContainer.end());
} // erase
template <
class Container,
class Pred
>
void erase_if(Container& ioContainer, Pred iPred)
{
ioContainer.erase(
std::remove_if(ioContainer.begin(),
ioContainer.end(),
iPred),
ioContainer.end());
} // erase_if
Упаковка спринтф
string example = function("<li value='%d'>Buffer at: 0x%08X</li>", 42, &some_obj);
// 'function' is one of the functions below: Format or stringf
Цель состоит в том, чтобы отделить форматирование от выходных данных без проблем со sprintf и тому подобным. Это не красиво, но очень полезно, особенно если ваши правила кодирования запрещают iostreams.
Вот версия, которая выделяет по мере необходимости, от Нила Баттерворта. [Просмотр истории изменений для версии Майка, которую я удалил как подмножество оставшихся двух. Он похож на Neil, за исключением того, что последний безопасен для исключительных ситуаций, используя vector вместо delete[]: ctor строки выдаст ошибку при выделении. Майк также использует ту же технику, показанную позже, чтобы определить размер заранее. -rp]
string Format( const char * fmt, ... ) {
const int BUFSIZE = 1024;
int size = BUFSIZE, rv = -1;
vector <char> buf;
do {
buf.resize( size );
va_list valist;
va_start( valist, fmt );
// if _vsnprintf() returns < 0, the buffer wasn't big enough
// so increase buffer size and try again
// NOTE: MSFT's _vsnprintf is different from C99's vsnprintf,
// which returns non-negative on truncation
// http://msdn.microsoft.com/en-us/library/1kt27hek.aspx
rv = _vsnprintf( &buf[0], size, fmt, valist );
va_end( valist );
size *= 2;
}
while( rv < 0 );
return string( &buf[0] );
}
Вот версия, которая заранее определяет необходимый размер, от Роджера Пейта. Для этого требуются доступные для записи std::strings, которые предоставляются популярными реализациями, но явно требуются в C++0x. [Просмотр истории изменений для версии Маркуса, которую я удалил, так как она немного отличается, но, по сути, является подмножеством нижеприведенного. -rp]
Реализация
void vinsertf(std::string& s, std::string::iterator it,
char const* fmt, int const chars_needed, va_list args
) {
using namespace std;
int err; // local error code
if (chars_needed < 0) err = errno;
else {
string::size_type const off = it - s.begin(); // save iterator offset
if (it == s.end()) { // append to the end
s.resize(s.size() + chars_needed + 1); // resize, allow snprintf's null
it = s.begin() + off; // iterator was invalidated
err = vsnprintf(&*it, chars_needed + 1, fmt, args);
s.resize(s.size() - 1); // remove snprintf's null
}
else {
char saved = *it; // save char overwritten by snprintf's null
s.insert(it, chars_needed, '\0'); // insert needed space
it = s.begin() + off; // iterator was invalidated
err = vsnprintf(&*it, chars_needed + 1, fmt, args);
*(it + chars_needed) = saved; // restore saved char
}
if (err >= 0) { // success
return;
}
err = errno;
it = s.begin() + off; // above resize might have invalidated 'it'
// (invalidation is unlikely, but allowed)
s.erase(it, it + chars_needed);
}
string what = stringf("vsnprintf: [%d] ", err);
what += strerror(err);
throw runtime_error(what);
}
Открытый интерфейс
std::string stringf(char const* fmt, ...) {
using namespace std;
string s;
va_list args;
va_start(args, fmt);
int chars_needed = vsnprintf(0, 0, fmt, args);
va_end(args);
va_start(args, fmt);
try {
vinsertf(s, s.end(), fmt, chars_needed, args);
}
catch (...) {
va_end(args);
throw;
}
va_end(args);
return s;
}
// these have nearly identical implementations to stringf above:
std::string& appendf(std::string& s, char const* fmt, ...);
std::string& insertf(std::string& s, std::string::iterator it,
char const* fmt, ...);
is_sorted
утилита, чтобы проверить контейнеры перед применением таких алгоритмов, как include
которые ожидают отсортированную запись:
template <
class FwdIt
>
bool is_sorted(FwdIt iBegin, FwdIt iEnd)
{
typedef typename std::iterator_traits<FwdIt>::value_type value_type;
return adjacent_find(iBegin, iEnd, std::greater<value_type>()) == iEnd;
} // is_sorted
template <
class FwdIt,
class Pred
>
bool is_sorted_if(FwdIt iBegin, FwdIt iEnd, Pred iPred)
{
if (iBegin == iEnd) return true;
FwdIt aIt = iBegin;
for (++aIt; aIt != iEnd; ++iBegin, ++aIt)
{
if (!iPred(*iBegin, *aIt)) return false;
}
return true;
} // is_sorted_if
Да, я знаю, было бы лучше отрицать предикат и использовать версию предиката adjacent_find
:)
Вот мой набор дополнительных утилит, созданных поверх оболочки std-algo boost.range'ish, которые могут вам понадобиться для некоторых функций. (написать тривиально, это интересный материал)
#pragma once
/** @file
@brief Defines various utility classes/functions for handling ranges/function objects
in addition to bsRange (which is a ranged version of the \<algorithm\> header)
Items here uses a STL/boost-style naming due to their 'templatised' nature.
If template variable is R, anything matching range_concept can be used.
If template variable is C, it must be a container object (supporting C::erase())
*/
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/smart_ptr.hpp>
namespace boost
{
struct use_default;
template<class T>
class iterator_range;
#pragma warning(disable: 4348) // redeclaration of template default parameters (this clashes with fwd-decl in boost/transform_iterator.hpp)
template <
class UnaryFunction
, class Iterator
, class Reference = use_default
, class Value = use_default
>
class transform_iterator;
template <
class Iterator
, class Value = use_default
, class Category = use_default
, class Reference = use_default
, class difference = use_default
>
class indirect_iterator;
template<class T>
struct range_iterator;
template <
class Incrementable
, class CategoryOrTraversal = use_default
, class difference = use_default
>
class counting_iterator;
template <class Predicate, class Iterator>
class filter_iterator;
}
namespace orz
{
/// determines if any value that compares equal exists in container
template<class R, class T>
inline bool contains(const R& r, const T& v)
{
return std::find(boost::begin(r), boost::end(r), v) != boost::end(r);
}
/// determines if predicate evaluates to true for any value in container
template<class R, class F>
inline bool contains_if(const R& r, const F& f)
{
return std::find_if(boost::begin(r), boost::end(r), f) != boost::end(r);
}
/// insert elements in range r at end of container c
template<class R, class C>
inline void insert(C& c, const R& r)
{
c.insert(c.end(), boost::begin(r), boost::end(r));
}
/// copy elements that match predicate
template<class I, class O, class P>
inline void copy_if(I i, I end, O& o, const P& p)
{
for (; i != end; ++i) {
if (p(*i)) {
*o = *i;
++o;
}
}
}
/// copy elements that match predicate
template<class R, class O, class P>
inline void copy_if(R& r, O& o, const P& p)
{
copy_if(boost::begin(r), boost::end(r), o, p);
}
/// erases first element that compare equal
template<class C, class T>
inline bool erase_first(C& c, const T& v)
{
typename C::iterator end = boost::end(c);
typename C::iterator i = std::find(boost::begin(c), end, v);
return i != c.end() ? c.erase(i), true : false;
}
/// erases first elements that match predicate
template<class C, class F>
inline bool erase_first_if(C& c, const F& f)
{
typename C::iterator end = boost::end(c);
typename C::iterator i = std::find_if(boost::begin(c), end, f);
return i != end ? c.erase(i), true : false;
}
/// erase all elements (doesn't deallocate memory for std::vector)
template<class C>
inline void erase_all(C& c)
{
c.erase(c.begin(), c.end());
}
/// erase all elements that compare equal
template<typename C, typename T>
int erase(C& c, const T& value)
{
int n = 0;
for (boost::range_iterator<C>::type i = boost::begin(c); i != boost::end(c);) {
if (*i == value) {
i = c.erase(i);
++n;
} else {
++i;
}
}
return n;
}
/// erase all elements that match predicate
template<typename C, typename F>
int erase_if(C& c, const F& f)
{
int n = 0;
for (boost::range_iterator<C>::type i = boost::begin(c); i != boost::end(c);) {
if (f(*i)) {
i = c.erase(i);
++n;
} else {
++i;
}
}
return n;
}
/// erases all consecutive duplicates from container (sort container first to get all)
template<class C>
inline int erase_duplicates(C& c)
{
boost::range_iterator<C>::type i = std::unique(c.begin(), c.end());
typename C::size_type n = std::distance(i, c.end());
c.erase(i, c.end());
return n;
}
/// erases all consecutive duplicates, according to predicate, from container (sort container first to get all)
template<class C, class F>
inline int erase_duplicates_if(C& c, const F& f)
{
boost::range_iterator<C>::type i = std::unique(c.begin(), c.end(), f);
typename C::size_type n = std::distance(i, c.end());
c.erase(i, c.end());
return n;
}
/// fill but for the second value in each pair in range
template<typename R, typename V>
inline void fill_second(R& r, const V& v)
{
boost::range_iterator<R>::type i(boost::begin(r)), end(boost::end(r));
for (; i != end; ++i) {
i->second = v;
}
}
/// applying function to corresponding pair through both ranges, min(r1.size(), r2,size()) applications
template<typename R1, typename R2, typename F>
void for_each2(R1& r1, R2& r2, const F& f)
{
boost::range_iterator<R1>::type i(boost::begin(r1)), i_end(boost::end(r1));
boost::range_iterator<R2>::type j(boost::begin(r2)), j_end(boost::end(r2));
for(;i != i_end && j != j_end; ++i, ++j) {
f(*i, *j);
}
}
/// applying function to corresponding pair through both ranges, min(r1.size(), r2,size()) applications
template<typename R1, typename R2, typename R3, typename F>
void for_each3(R1& r1, R2& r2, R3& r3, const F& f)
{
boost::range_iterator<R1>::type i(boost::begin(r1)), i_end(boost::end(r1));
boost::range_iterator<R2>::type j(boost::begin(r2)), j_end(boost::end(r2));
boost::range_iterator<R3>::type k(boost::begin(r3)), k_end(boost::end(r3));
for(;i != i_end && j != j_end && k != k_end; ++i, ++j, ++k) {
f(*i, *j, *k);
}
}
/// applying function to each possible permutation of objects, r1.size() * r2.size() applications
template<class R1, class R2, class F>
void for_each_permutation(R1 & r1, R2& r2, const F& f)
{
typedef boost::range_iterator<R1>::type R1_iterator;
typedef boost::range_iterator<R2>::type R2_iterator;
R1_iterator end_1 = boost::end(r1);
R2_iterator begin_2 = boost::begin(r2);
R2_iterator end_2 = boost::end(r2);
for(R1_iterator i = boost::begin(r1); i != end_1; ++i) {
for(R2_iterator j = begin_2; j != end_2; ++j) {
f(*i, *j);
}
}
}
template <class R>
inline boost::iterator_range<boost::indirect_iterator<typename boost::range_iterator<R>::type > >
make_indirect_range(R& r)
{
return boost::iterator_range<boost::indirect_iterator<typename boost::range_iterator<R>::type > > (r);
}
template <class R, class F>
inline boost::iterator_range<boost::transform_iterator<F, typename boost::range_iterator<R>::type> >
make_transform_range(R& r, const F& f)
{
return boost::iterator_range<boost::transform_iterator<F, typename boost::range_iterator<R>::type> >(
boost::make_transform_iterator(boost::begin(r), f),
boost::make_transform_iterator(boost::end(r), f));
}
template <class T>
inline boost::iterator_range<boost::counting_iterator<T> >
make_counting_range(T begin, T end)
{
return boost::iterator_range<boost::counting_iterator<T> >(
boost::counting_iterator<T>(begin), boost::counting_iterator<T>(end));
}
template <class R, class F>
inline boost::iterator_range<boost::filter_iterator<F, typename boost::range_iterator<R>::type> >
make_filter_range(R& r, const F& f)
{
return boost::iterator_range<boost::filter_iterator<F, typename boost::range_iterator<R>::type> >(
boost::make_filter_iterator(f, boost::begin(r), boost::end(r)),
boost::make_filter_iterator(f, boost::end(r), boost::end(r)));
}
namespace detail {
template<class T>
T* get_pointer(T& p) {
return &p;
}
}
/// compare member function/variable equal to value. Create using @ref mem_eq() to avoid specfying types
template<class P, class V>
struct mem_eq_type
{
mem_eq_type(const P& p, const V& v) : m_p(p), m_v(v) { }
template<class T>
bool operator()(const T& a) const {
using boost::get_pointer;
using orz::detail::get_pointer;
return (get_pointer(a)->*m_p) == m_v;
}
P m_p;
V m_v;
};
template<class P, class V>
mem_eq_type<P,V> mem_eq(const P& p, const V& v)
{
return mem_eq_type<P,V>(p, v);
}
/// helper macro to define function objects that compare member variables of a class
#define ORZ_COMPARE_MEMBER(NAME, OP) \
template <class P> \
struct NAME##_type \
{ \
NAME##_type(const P&p) : m_p(p) {} \
template<class T> \
bool operator()(const T& a, const T& b) const { \
return (a.*m_p) OP (b.*m_p); \
} \
P m_p; \
}; \
template <class P> \
NAME##_type<P> NAME(const P& p) { return NAME##_type<P>(p); }
#define ORZ_COMPARE_MEMBER_FN(NAME, OP) \
template <class P> \
struct NAME##_type \
{ \
NAME##_type(const P&p) : m_p(p) {} \
template<class T> \
bool operator()(const T& a, const T& b) const { \
return (a.*m_p)() OP (b.*m_p)(); \
} \
P m_p; \
}; \
template <class P> \
NAME##_type<P> NAME(const P& p) { return NAME##_type<P>(p); }
/// helper macro to wrap range functions as function objects (value return)
#define ORZ_RANGE_WRAP_VALUE_2(FUNC, RESULT) \
struct FUNC##_ \
{ \
typedef RESULT result_type; \
template<typename R, typename F> \
inline RESULT operator() (R& r, const F& f) const \
{ \
return FUNC(r, f); \
} \
};
/// helper macro to wrap range functions as function objects (void return)
#define ORZ_RANGE_WRAP_VOID_2(FUNC) \
struct FUNC##_ \
{ \
typedef void result_type; \
template<typename R, typename F> \
inline void operator() (R& r, const F& f) const \
{ \
FUNC(r, f); \
} \
};
/// helper macro to wrap range functions as function objects (void return, one argument)
#define ORZ_RANGE_WRAP_VOID_1(FUNC) \
struct FUNC##_ \
{ \
typedef void result_type; \
template<typename R> \
inline void operator() (R& r) const \
{ \
FUNC(r); \
} \
};
ORZ_RANGE_WRAP_VOID_2(for_each);
ORZ_RANGE_WRAP_VOID_1(erase_all);
ORZ_RANGE_WRAP_VALUE_2(contains, bool);
ORZ_RANGE_WRAP_VALUE_2(contains_if, bool);
ORZ_COMPARE_MEMBER(mem_equal, ==)
ORZ_COMPARE_MEMBER(mem_not_equal, !=)
ORZ_COMPARE_MEMBER(mem_less, <)
ORZ_COMPARE_MEMBER(mem_greater, >)
ORZ_COMPARE_MEMBER(mem_lessequal, <=)
ORZ_COMPARE_MEMBER(mem_greaterequal, >=)
ORZ_COMPARE_MEMBER_FN(mem_equal_fn, ==)
ORZ_COMPARE_MEMBER_FN(mem_not_equal_fn, !=)
ORZ_COMPARE_MEMBER_FN(mem_less_fn, <)
ORZ_COMPARE_MEMBER_FN(mem_greater_fn, >)
ORZ_COMPARE_MEMBER_FN(mem_lessequal_fn, <=)
ORZ_COMPARE_MEMBER_FN(mem_greaterequal_fn, >=)
#undef ORZ_COMPARE_MEMBER
#undef ORZ_RANGE_WRAP_VALUE_2
#undef ORZ_RANGE_WRAP_VOID_1
#undef ORZ_RANGE_WRAP_VOID_2
}
Кажется, мне нужен декартово произведение, например, {A, B}, {1, 2} -> {(A,1), (A,2), (B,1), (B,2)}
// OutIt needs to be an iterator to a container of std::pair<Type1, Type2>
template <typename InIt1, typename InIt2, typename OutIt>
OutIt
cartesian_product(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt out)
{
for (; first1 != last1; ++first1)
for (InIt2 it = first2; it != last2; ++it)
*out++ = std::make_pair(*first1, *it);
return out;
}
Глядя на мой stl_util.h
многие классики (функции удаления, copy_if
), а также этот (вероятно, также довольно распространенный, но я пока не вижу его в ответах) для поиска по карте и возврата либо найденного значения, либо значения по умолчанию, аля get
в питоне dict
:
template<typename K, typename V>
inline V search_map(const std::map<K, V>& mapping,
const K& key,
const V& null_result = V())
{
typename std::map<K, V>::const_iterator i = mapping.find(key);
if(i == mapping.end())
return null_result;
return i->second;
}
Использование по умолчанию null_result
построенный по умолчанию V
так же, как поведение std::map
"s operator[]
, но это полезно, когда карта является const (типичной для меня), или если построенный по умолчанию V не подходит для использования.
//! \brief Fills reverse_map from map, so that all keys of map
// become values of reverse_map and all values become keys.
//! \note This presumes that there is a one-to-one mapping in map!
template< typename T1, typename T2, class TP1, class TA1, class TP2, class TA2 >
inline void build_reverse_map( const std::map<T1,T2,TP1,TA1>& map
, std::map<T2,T1,TP2,TA2>& reverse_map)
{
typedef std::map<T1,T2,TP1,TA1> map_type;
typedef std::map<T2,T1,TP2,TA2> r_map_type;
typedef typename r_map_type::value_type r_value_type;
for( typename map_type::const_iterator it=map.begin(),
end=map.end(); it!=end; ++it ) {
const r_value_type v(it->second,it->first);
const bool was_new = reverse_map.insert(v).second;
assert(was_new);
}
}
Я бы назвал такую функцию добавления по имени и использовал бы оператор +=, оператор *= и так далее для поэлементных операций, таких как:
template<typename X> inline void operator+= (std::vector<X>& vec1, const X& value)
{
std::transform( vec1.begin(), vec1.end(), vec1.begin(), std::bind2nd(std::plus<X>(),value) );
}
template<typename X> inline void operator+= (std::vector<X>& vec1, const std::vector<X>& vec2)
{
std::transform( vec1.begin(), vec1.end(), vec2.begin(), vec1.begin(), std::plus<X>() );
}
некоторые другие простые и очевидные обертки, как подразумевалось ранее:
template<typename X> inline void sort_and_unique(std::vector<X> &vec)
{
std::sort( vec.begin(), vec.end() );
vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() );
}
template<typename X> inline void clear_vec(std::vector<X> &vec)
{
std::vector<X>().swap(vec);
}
template<typename X> inline void trim_vec(std::vector<X> &vec, std::size_t new_size)
{
if (new_size<vec.size())
std::vector<X>(vec.begin(),vec.begin() + new_size).swap(vec);
else
std::vector<X>(vec).swap(vec);
}
ИМО там должно быть больше функциональности для pair
:
#ifndef pair_iterator_h_
#define pair_iterator_h_
#include <boost/iterator/transform_iterator.hpp>
#include <functional>
#include <utility>
// pair<T1, T2> -> T1
template <typename PairType>
struct PairGetFirst : public std::unary_function<PairType, typename PairType::first_type>
{
typename typename PairType::first_type& operator()(PairType& arg) const
{ return arg.first; }
const typename PairType::first_type& operator()(const PairType& arg) const
{ return arg.first; }
};
// pair<T1, T2> -> T2
template <typename PairType>
struct PairGetSecond : public std::unary_function<PairType, typename PairType::second_type>
{
typename PairType::second_type& operator()(PairType& arg) const
{ return arg.second; }
const typename PairType::second_type& operator()(const PairType& arg) const
{ return arg.second; }
};
// iterator over pair<T1, T2> -> iterator over T1
template <typename Iter>
boost::transform_iterator<PairGetFirst<typename std::iterator_traits<Iter>::value_type>, Iter>
make_first_iterator(Iter i)
{
return boost::make_transform_iterator(i,
PairGetFirst<typename std::iterator_traits<Iter>::value_type>());
}
// iterator over pair<T1, T2> -> iterator over T2
template <typename Iter>
boost::transform_iterator<PairGetSecond<typename std::iterator_traits<Iter>::value_type>, Iter>
make_second_iterator(Iter i)
{
return boost::make_transform_iterator(i,
PairGetSecond<typename std::iterator_traits<Iter>::value_type>());
}
// T1 -> pair<T1, T2>
template <typename FirstType, typename SecondType>
class InsertIntoPair1st : public std::unary_function<FirstType, std::pair<FirstType, SecondType> >
{
public:
InsertIntoPair1st(const SecondType& second_element) : second_(second_element) {}
result_type operator()(const FirstType& first_element)
{
return result_type(first_element, second_);
}
private:
SecondType second_;
};
// T2 -> pair<T1, T2>
template <typename FirstType, typename SecondType>
class InsertIntoPair2nd : public std::unary_function<SecondType, std::pair<FirstType, SecondType> >
{
public:
InsertIntoPair2nd(const FirstType& first_element) : first_(first_element) {}
result_type operator()(const SecondType& second_element)
{
return result_type(first_, second_element);
}
private:
FirstType first_;
};
#endif // pair_iterator_h_
Вставьте новый элемент и верните его, что полезно для семантики простых перемещений, таких как push_back(c).swap(value)
и связанные случаи.
template<class C>
typename C::value_type& push_front(C& container) {
container.push_front(typename C::value_type());
return container.front();
}
template<class C>
typename C::value_type& push_back(C& container) {
container.push_back(typename C::value_type());
return container.back();
}
template<class C>
typename C::value_type& push_top(C& container) {
container.push(typename C::value_type());
return container.top();
}
Взять и вернуть предмет:
template<class C>
typename C::value_type pop_front(C& container) {
typename C::value_type copy (container.front());
container.pop_front();
return copy;
}
template<class C>
typename C::value_type pop_back(C& container) {
typename C::value_type copy (container.back());
container.pop_back();
return copy;
}
template<class C>
typename C::value_type pop_top(C& container) {
typename C::value_type copy (container.top());
container.pop();
return copy;
}
Дублируйте строку с *:
std::string operator*(std::string s, size_t n)
{
std::stringstream ss;
for (size_t i=0; i<n; i++) ss << s;
return ss.str();
}
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }
Если вам нужно использовать много функций, которые принимают указатель + количество байтов, это всегда просто
fun(vec.data(), bytesize(vec));
Не уверен, что они соответствуют стандартным упаковщикам, но мои часто используемые вспомогательные функции:
void split(string s, vector<string> parts, string delims);
string join(vector<string>& parts, string delim);
int find(T& array, const V& value);
void assert(bool condition, string message);
V clamp(V value, V minvalue, V maxvalue);
string replace(string s, string from, string to);
const char* stristr(const char* a,const char*b);
string trim(string str);
T::value_type& dyn(T& array,int index);
T и V здесь являются шаблонными аргументами. Последняя функция работает так же, как []-оператор, но с автоматическим изменением размера для соответствия необходимому индексу.
Подобно тому, что люди публиковали ранее, у меня есть удобные перегрузки алгоритмов для упрощения передачи аргументов итератора. Я называю алгоритмы так:
for_each(iseq(vec), do_it());
Я перегрузил все алгоритмы так, что они принимают один параметр типа input_sequence_range<>
вместо двух входных итераторов (ввод, как во всем, что не является просто выводом).
template<typename In>
struct input_sequence_range
: public std::pair<In,In>
{
input_sequence_range(In first, In last)
: std::pair<In,In>(first, last)
{ }
};
А это как iseq()
работает:
template<typename C>
input_sequence_range<typename C::const_iterator> iseq(const C& c)
{
return input_sequence_range<typename C::const_iterator>(c.begin(),
c.end());
}
Точно так же у меня есть специализации для
- const_iterators
- указатели (примитивные массивы)
- потоковые итераторы
- любой диапазон [начало, конец) только для единообразного использования: используйте iseq() для всего
Один из моих любимых это Transposer
который находит транспонирование кортежа контейнеров одинакового размера. То есть, если у вас есть tuple<vector<int>,vector<float>>
, он превращает его в vector<tuple<int, float>>
, Пригодится в программировании XML. Вот как я это сделал.
#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include <algorithm>
#include <stdexcept>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/type_traits.hpp>
using namespace boost;
template <class TupleOfVectors>
struct GetTransposeTuple;
template <>
struct GetTransposeTuple<tuples::null_type>
{
typedef tuples::null_type type;
};
template <class TupleOfVectors>
struct GetTransposeTuple
{
typedef typename TupleOfVectors::head_type Head;
typedef typename TupleOfVectors::tail_type Tail;
typedef typename
tuples::cons<typename remove_reference<Head>::type::value_type,
typename GetTransposeTuple<Tail>::type> type;
};
template <class TupleOfVectors,
class ValueTypeTuple =
typename GetTransposeTuple<TupleOfVectors>::type,
unsigned int TUPLE_INDEX = 0>
struct Transposer
: Transposer <typename TupleOfVectors::tail_type,
ValueTypeTuple,
TUPLE_INDEX + 1>
{
typedef typename remove_reference<typename TupleOfVectors::head_type>::type
HeadContainer;
typedef typename TupleOfVectors::tail_type Tail;
typedef Transposer<Tail, ValueTypeTuple, TUPLE_INDEX + 1> super;
typedef std::vector<ValueTypeTuple> Transpose;
Transposer(TupleOfVectors const & tuple)
: super(tuple.get_tail()),
head_container_(tuple.get_head()),
head_iter_(head_container_.begin())
{}
Transpose get_transpose ()
{
Transpose tran;
tran.reserve(head_container_.size());
for(typename HeadContainer::const_iterator iter = head_container_.begin();
iter != head_container_.end();
++iter)
{
ValueTypeTuple vtuple;
this->populate_tuple(vtuple);
tran.push_back(vtuple);
}
return tran;
}
private:
HeadContainer const & head_container_;
typename HeadContainer::const_iterator head_iter_;
protected:
void populate_tuple(ValueTypeTuple & vtuple)
{
if(head_iter_ == head_container_.end())
throw std::runtime_error("Container bound exceeded.");
else
{
vtuple.get<TUPLE_INDEX>() = *head_iter_++;
super::populate_tuple (vtuple);
}
}
};
template <class ValueTypeTuple,
unsigned int INDEX>
struct Transposer <tuples::null_type, ValueTypeTuple, INDEX>
{
void populate_tuple(ValueTypeTuple &) {}
Transposer (tuples::null_type const &) {}
};
template <class TupleOfVectors>
typename Transposer<TupleOfVectors>::Transpose
transpose (TupleOfVectors const & tupleofv)
{
return Transposer<TupleOfVectors>(tupleofv).get_transpose();
}
int main (void)
{
typedef std::vector<int> Vint;
typedef std::list<float> Lfloat;
typedef std::vector<long> Vlong;
Vint vint;
Lfloat lfloat;
Vlong vlong;
std::generate_n(std::back_inserter(vint), 10, rand);
std::generate_n(std::back_inserter(lfloat), 10, rand);
std::generate_n(std::back_inserter(vlong), 10, rand);
typedef tuples::tuple<Vint, Lfloat, Vlong> TupleOfV;
typedef GetTransposeTuple<TupleOfV>::type TransposeTuple;
Transposer<TupleOfV>::Transpose tran =
transpose(make_tuple(vint, lfloat, vlong));
// Or alternatively to avoid copying
// transpose(make_tuple(ref(vint), ref(lfloat), ref(vlong)));
std::copy(tran.begin(), tran.end(),
std::ostream_iterator<TransposeTuple>(std::cout, "\n"));
return 0;
}
Неупорядоченное стирание для std::vector
, Самый эффективный способ стереть элемент из vector
но это не сохраняет порядок элементов. Я не видел смысла распространять его на другие контейнеры, так как у большинства нет одинакового штрафа за удаление предметов из середины. Это похоже на некоторые другие шаблоны уже опубликованы, но он использует std::swap
перемещать предметы вместо копирования.
template<typename T>
void unordered_erase(std::vector<T>& vec, const typename std::vector<T>::iterator& it)
{
if (it != vec.end()) // if vec is empty, begin() == end()
{
std::swap(vec.back(), *it);
vec.pop_back();
}
}
Signum возвращает знак типа. Возвращает -1
для негатива, 0
для нуля и 1
для позитива.
template <typename T>
int signum(T val)
{
return (val > T(0)) - (val < T(0));
}
Зажим довольно понятен, он зажимает значение так, чтобы оно находилось в заданном диапазоне. Меня поражает, что стандартная библиотека включает min
а также max
но нет clamp
template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
return value < lower ? lower : (value > upper ? upper : value);
}