¿Qué funciones contenedoras de la biblioteca estándar de C ++ utiliza?

81

Esta pregunta , formulada esta mañana, me hizo preguntarme qué características cree que faltan en la biblioteca estándar de C ++ y cómo ha logrado llenar los vacíos con funciones contenedoras. Por ejemplo, mi propia biblioteca de utilidades tiene esta función para agregar vectores:

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;
}

y este para borrar (más o menos) cualquier tipo, particularmente útil para cosas como std :: stack:

template <class C>
void Clear( C & c ) {
    c = C();
}

Tengo algunas más, pero me interesan ¿cuáles usas? Limite las respuestas a las funciones contenedoras , es decir, no más de un par de líneas de código.

anon
fuente
9
¿Cuenta que envolví la mayor parte del algoritmo STL para actuar en contenedores completos en lugar de rangos, solo porque jugar con los iteradores es un error tan común :)?
Matthieu M.
2
@Billy en realidad, CW no es realmente una excusa para hacer preguntas subjetivas. Cambiaré el título, lo que debería hacer feliz a la gente.
4
@kts: Dado que vector :: insert tiene iteradores de acceso aleatorio, una buena implementación usará el despacho en tiempo de compilación para hacerlo por sí mismo.
4
¿No es mejor escribir c.swap(C())para limpiar un contenedor?
Alexandre C.
5
@Alexandre: Eso no está permitido: enlaza una referencia temporal a una no constante. C (). Swap (c) funcionaría.

Respuestas:

37

boost :: matriz

contiene (contenedor, val) (bastante simple, pero conveniente).

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 (comienzo, final, valor)

Una versión más rápida de std :: remove con la excepción de que no conserva el orden de los objetos restantes.

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; 
}

(en el caso de un vector de tipos de pod (int, float, etc.) y se eliminan casi todos los objetos, std :: remove podría ser más rápido).

Viktor Sehr
fuente
4
¿Alguien más piensa que contiene necesita una tercera plantilla ( bool sorted=false) y una especialización cuando sorted==truellamar en binary_searchlugar de find?
KitsuneYMG
6
@kts: cuando sepa que el contenedor está ordenado, simplemente llame a binary_search directamente.
2
boost :: array STL equivalente está disponible en el espacio de nombres tr1 en los compiladores más recientes (incluso codewarrior): std :: tr1 :: array <>
Klaim
36

Muy a menudo, uso el vector como un conjunto de elementos sin un orden en particular (y, obviamente, cuando no necesito verificaciones rápidas de si este elemento está en el conjunto). En estos casos, llamar a erase () es una pérdida de tiempo, ya que reordenará los elementos y no me importa el orden. Ahí es cuando la función O (1) a continuación resulta útil: simplemente mueva el último elemento a la posición del que desea eliminar:

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}
sbk
fuente
Buena. No había pensado en hacerlo así ... :) Debería haber una envoltura de 'Bolsa' (similar a la pila y la cola) que acelera estas operaciones cuando el orden no es importante.
Macke
12
Y en C ++ 0x, v[index] = st::move(v.back()); v.pop_back();es tan eficiente como parece.
GManNickG
@Matthieu: Mira la marca de tiempo en esos. : PI se dio cuenta un par de horas después, mucho más allá del tiempo de edición permitido.
GManNickG
@GMan: ¿estás seguro de esto? me parece que v.pop_back () podría resultar en un comportamiento indefinido ya que se llamará al destructor.
Viktor Sehr
1
@Viktor: Move-semántica no significa "Tomo tus recursos y eso es todo", quieren decir "Tomo tus recursos y te pongo en un estado sin recursos". En otras palabras, su semántica de movimientos necesita asegurarse de que el objeto se pueda destruir de forma segura; bastante fácil de hacer, simplemente establezca punteros en nulo.
GManNickG
26
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&);
};

Ok, ya que parece que esto no es tan sencillo como pensaba, aquí hay una explicación:
en su constructor temp_valuealmacena una referencia a una variable y una copia del valor original de la variable. En su destructor, restaura la variable referenciada a su valor original. Por lo tanto, no importa lo que hizo con la variable entre construcción y destrucción, se restablecerá cuando el temp_valueobjeto salga del alcance.
Úselo así:

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
}

Aquí hay otro enfoque que usa el truco de protección de alcance:

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);
}

Es solo un poco más de código de placa de caldera, pero permite un uso más limpio:

#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);
}

Sin embargo, elimina su capacidad para ser utilizado en una clase.


Aquí hay una tercera forma de lograr el mismo efecto (que no sufre los problemas de lanzar potencialmente destructores):

Implementación:

//none -- it is built into the language

Uso:

#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);
}
GManNickG
fuente
1
@Billy: Es para restaurar el valor más tarde, automáticamente. (Y val debería ser eliminado del ctor.)
Lo siento, todavía estoy perdido (soy nuevo en C ++), ¿alguien puede simplificarlo?
dreamlax
1
@dreamlax: agregué un texto descriptivo que lo describe a la respuesta. ¿Es comprensible ahora o debería profundizar en los detalles?
sbi
1
oh, el segundo es bastante inteligente.
jalf
1
Hmm, ¿cuál es un caso de uso de la vida real para esto?
paulm
22

No es realmente un envoltorio, pero el infame desaparecido copy_if. Desde aqui

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;
}
Glen
fuente
2
No responde la pregunta, no es un contenedor para stdlib.
10
@Roger Pate, sí, lo sé, por eso la respuesta comienza con las palabras "No es realmente un envoltorio, pero ...".
Glen
1
Detalle de implementación de @Roger. Si realmente lo desea, puede implementarlo en términos de remove_copy_if(). : p
wilhelmtell
18
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;}
sbi
fuente
2
Yo también tengo estos. :) +1 Por lo que vale, solo necesitas dos (deshazte de las versiones const). Cuando la matriz es constante, Tserá const Uy obtendrá la función deseada.
GManNickG
@GMan: Hubo alguna versión de GCC que no compilaría algún código solo con las no constversiones, por eso las constversiones están ahí. Dado que podría haber sido un error de esa versión de GCC en particular, los eliminaré.
sbi
1
@Marcus: Estos son mucho más antiguos que Boost.Range. :)
sbi
4
@Roger: envuelve las matrices para que se usen con la lib estándar. Ahí tienes. :)
sbi
2
@Stacked, @sbe: las matrices nunca se pasan por valor, independientemente de si hay un &. El &está allí para permitir tipo deducción de la longitud de la matriz.
Mankarse
12

A veces me siento como si estuviera en begin()y end()demonios. Me gustaría tener algunas funciones como:

template<typename T>
void sort(T& x)
{
    std::sort(x.begin(), x.end());
}

y otros similares para std::find, std::for_eachy básicamente todos los algoritmos de STL.

Siento que sort(x)es mucho más rápido de leer / entender que sort(x.begin(), x.end()).

Inverso
fuente
1
insinuación; use ´sort (boost :: begin (x), boost: end (x)); ´ en su lugar, y también podrá ordenar matrices.
Viktor Sehr
4
Boost.Range v2 tiene adaptadores como este para toda la biblioteca estándar.
ildjarn
9

Ya no uso este casi tanto, pero solía ser un elemento básico:

template<typename T>
std::string make_string(const T& data) {
    std::ostringstream stream;
    stream << data;
    return stream.str();
}

Se actualizará con más a medida que los recuerde. :PAG

Jon Purdy
fuente
3
Jeje, una especie de atajo para boost::lexical_cast<t, t>.
Billy ONeal
1
sí, es genial si no quieres impulsar un proyecto
Steve
11
@BillyONeal: Esa es la razón por la que ya no lo uso. @Steve: Esa es la razón por la que todavía lo uso.
Jon Purdy
Sería innecesariamente caro llamarlo en a char*o a std::string. ¿Quizás una especialización en plantillas está en orden?
wilhelmtell
Si mal no recuerdo, boost::lexical_casttiene un montón de especializaciones y comprobaciones de errores. Sin embargo, para clasificar el número impar, esto funciona bien.
Jon Purdy
9

La función de utilidad en la caja de herramientas de todos es, por supuesto copy_if. Aunque no es realmente un envoltorio.

Otro ayudante que uso habitualmente es deleterun functor con el que utilizo std::for_eachpara eliminar todos los punteros de un contenedor.

[editar] Buscando en mi "sth.h" también encontré vector<wstring> StringSplit(wstring const&, wchar_t);

MSalters
fuente
+1 para eliminadores de funciones. Mi functor de eliminación funciona bien con la mayoría de los contenedores, sin embargo, he estado jugando para que funcione con std :: map's donde la clave o el valor es un puntero. Intenté usar rasgos tipográficos para resolver el problema, pero no llegué muy lejos debido a limitaciones de tiempo. ¿Ha resuelto este problema o lo considera un problema?
Glen
2
@Matthieu M., desafortunadamente, no todos podemos usar boost.
Glen
@Glen: Todos estos problemas se pueden resolver utilizando punteros inteligentes, preferiblemente (pero no necesariamente) de boost. Como efecto secundario importante, los contenedores con punteros a objetos asignados dinámicamente de repente también se vuelven seguros para excepciones.
sbi
@Glen aquí, aquí para proyectos que no estipulan bibliotecas más que STD, hasta e incluyendo Boost o TR1.
Wheaties
7
@las desafortunadas víctimas de la estupidez corporativa: obtenga una excepción para las bibliotecas internas, luego importe las partes útiles de Boost a la biblioteca interna. Incluso en política, todos los problemas pueden resolverse con otro nivel de indirecta.
MSalters
9

Tengo un encabezado que pone lo siguiente en el espacio de nombres "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 &ltrim(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;
}
Evan Terán
fuente
2
Eso split()traga cualquier error que ocurra std::getline(), devolviendo silenciosamente un vector que es demasiado corto.
sbi
por supuesto, debe verificar el size()resultado antes de recuperar sus cadenas.
Evan Teran
¿Y cómo sabría cuántas cadenas debería tener el resultado?
sbi
2
@sbi: Su comentario ha despertado mi interés sobre lo que realmente podría salir mal (aparte de que la cadena simplemente no tiene suficientes tokens para obtener) con el bucle stringstream/ getline. He hecho una pregunta al respecto aquí: stackoverflow.com/questions/2562906/…
Evan Teran
2
@Evan: Me corrijo. Vea mi comentario en stackoverflow.com/2563542#2563542 . Lo siento.
sbi
8

El erasealgoritmo infamemente perdido :

  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
Matthieu M.
fuente
+1, estaba a punto de publicar el mío equivalente exacto. Aunque lo he llamado remove_erase (...)
Viktor Sehr
2
El único problema con esto es que rompe el lenguaje semántico de borrar-eliminar esperado en el STL. Necesita el lenguaje borrar-eliminar con cualquier algoritmo que tenga semántica de eliminación, no solo std::remove. Por ejemplo std::unique,.
Billy ONeal
Bueno, tengo adaptaciones de contenedores de la mayoría de los algoritmos STL para esto :) Pero este es el que más uso, por lo general, si quiero unicidad, uso un setpara empezar.
Matthieu M.
@Matthieu M .: Solo tenga en cuenta que al hacer esto, las personas que trabajan con el STL todo el tiempo tendrán la alarma sonando en sus cabezas. . Realmente no tiene nada de malo, pero no es algo que haría si tuviera que compartir mi código con muchos programadores. Solo mis 2 centavos.
Billy ONeal
1
@Billy: por eso lo llamé borrar y no eliminar. No hay mucho que pueda hacer además de dejarles consultar el código. Afortunadamente, con IDE moderno, la definición está a solo un clic de distancia :)
Matthieu M.
7

Envolviendo sprintf

string example = function("<li value='%d'>Buffer at: 0x%08X</li>", 42, &some_obj);
// 'function' is one of the functions below: Format or stringf

El objetivo es desacoplar el formato de la salida sin tener problemas con sprintf y sus similares. No es bonito, pero es muy útil, especialmente si sus pautas de codificación prohíben iostreams.


Aquí hay una versión que asigna según sea necesario, de Neil Butterworth. [Ver el historial de revisión de la versión de Mike, que eliminé como un subconjunto de las dos restantes. Es similar al de Neil, excepto que el último es seguro para excepciones al usar vector en lugar de delete []: el ctor de la cadena arrojará un error de asignación. Mike también usa la misma técnica que se muestra más adelante para determinar el tamaño al frente. –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] );
}

Aquí hay una versión que determina el tamaño necesario por adelantado, de Roger Pate . Esto requiere std :: strings grabables, que son proporcionados por implementaciones populares, pero son explícitamente requeridos por C ++ 0x. [Ver el historial de revisión de la versión de Marcus, que eliminé porque es un poco diferente pero esencialmente un subconjunto de lo siguiente. –RP]

Implementación

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);
}

Interfaz pública

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, ...);
Roger Pate
fuente
@Neil: De man vsnprintf: "Estas funciones devuelven el número de caracteres impresos ... o un valor negativo si se produce un error de salida, a excepción de snprintf()y vsnprintf(), que devuelven el número de caracteres que se han impreso si el n eran ilimitadas ... "De ahí la llamada ficticia con un búfer 0 para medir el tamaño del búfer necesario.
Mike DeSimone
@Checkers: Ah, Boost. Un potencial tan grande que tampoco me dejan usar. Algún día, con suerte. De todos modos, ¿Boost se ha vuelto lo suficientemente grande como para ser imposible de entender por completo todavía? Sería feliz solo por conseguirlo boost::spirit.
Mike DeSimone
Este es en realidad código de Windows, de MSDN "Para _vsnprintf, si el número de bytes para escribir excede el búfer, se escriben los bytes de recuento y se devuelve –1". pero lo porté a Linux. No puedo recordar si mi aplicación de Linux realmente usa esto, o si la he probado para tamaños de búfer grandes allí, debo hacerlo. Gracias.
1
Si usa el código de Windows de todos modos, continúe y utilícelo _vscprintfpara determinar el tamaño necesario del búfer.
Smerlin
6

La is_sortedutilidad, para probar contenedores antes de aplicar algoritmos como los includeque esperan una entrada ordenada:

  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

Sí, lo sé, sería mejor negar el predicado y usar la versión del predicado de adjacent_find:)

Matthieu M.
fuente
1
siempre y cuando solo haga la prueba en un assert(): p
wilhelmtell
No deberías usar la notación húngara.
the_drow
3
@the_drow: muchas gracias por este comentario útil :) No soy tan fanático de eso, pero es un requisito donde trabajo ... Desde entonces me he quitado el hábito, no te preocupes por mi alma;)
Matthieu M.
3
//! \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);
    }
}
sbi
fuente
Prefiero usar la Boost.Bimapbiblioteca (o Boost.MultiIndexpara situaciones más complejas)
Matthieu M.
1
No lo entiendo, ¿por qué afirmar ()?
Viktor Sehr
@Viktor: para asegurarse de que no haya claves duplicadas en reverse_map. Considere que maptiene (1 -> "uno"; 2 -> "uno") reverse_mapobtendrá un elemento ("uno" -> 1). La afirmación captará eso. Ver también:
bijection
3
Por cierto, sbi, tener un código con efectos secundarios dentro de un assert () lo morderá bastante una vez que compile con NDEBUG y los assert () s se eliminen por completo.
sbk
2
gah, después de la actualización, mi primer comentario parece realmente estúpido, stackoverflow es el número 1 cuando busco en Google mi nombre, así que espero que ningún futuro empleador vea esto =)
Viktor Sehr
3

Mirando mi stl_util.h, muchos de los clásicos (funciones de eliminación copy_if), y también este (probablemente también bastante común, pero no lo veo dado en las respuestas hasta ahora) para buscar en un mapa y devolver el valor encontrado o por defecto, ala geten Python 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;
   }

Usando el predeterminado null_result de un construido por defecto Ves muy parecido al comportamiento de std::mapun operator[], pero esto es útil cuando el mapa es constante (común para mí), o si el V construido por defecto no es lo correcto para usar.

Jack Lloyd
fuente
¿Qué pasa si V es un int o float o alguna otra primitiva?
Inverso
La inicialización de valor vacía funciona para tipos básicos en C ++. Para enteros y flotantes, eso haría que el null_result predeterminado sea 0.
Jack Lloyd
3

Aquí está mi conjunto de utilidades adicionales, construido sobre un contenedor boost.range'ish std-algo que podría necesitar para algunas funciones. (eso es trivial de escribir, esto es lo interesante)

#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
}
Macke
fuente
+1 para for_each_permutation (...) , principalmente porque he escrito una envoltura similar =). Pero, ¿por qué erase_duplicates (...) devuelve un int firmado?
Viktor Sehr
¡Hola Viktor! Creo que debería haber visto for_each_permutation en su trabajo anterior. ;) erase_duplicates devuelve el número de elementos borrados, lo cual es útil para registrar y depurar.
Macke
Hmm podría haberlo examinado sin darse cuenta de lo que hace ;) , de todos modos, entiendo por qué devuelve un número entero, simplemente no entiendo por qué el número entero está firmado (o para ser más específico; ¿por qué no está sin firmar )?
Viktor Sehr
¡Ah! Solo pereza de mi parte :-P. size_t es el tipo apropiado.
Macke
3

Parece que necesito un producto cartesiano, por ejemplo, {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;
}
rlbond
fuente
Tenga en cuenta que InIt2 debe ser un iterador de avance en lugar de un iterador de entrada. Los iteradores de entrada no son adecuados para varias pasadas.
2

Llamaría a dicha función de adición por su nombre y usaría operator + =, operator * = y así sucesivamente para operaciones de elementos, como:

    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>() );
    }

algunos otros envoltorios simples y obvios como se implicó antes:

    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);
    }
danés
fuente
7
Estos operadores son un muy buen ejemplo de por qué la sobrecarga de operadores se debe realizar en raras ocasiones. Pensé vec+=valque agregaría un valor al vector. (Consulte stackoverflow.com/questions/2551775/. ) Ahora que he visto su implementación, creo que también es una interpretación correcta del significado de +=. Yo no sé cuál sería correcto o incorrecto, por lo que es probablemente igual de bien que no tenemos +=para std::vector.
sbi
1
@sbi estoy de acuerdo. Encuentro que me operator+()falta una asombrosa comprensión inicial del estándar. Por lo general, espero una operación O (1) en todos los lugares donde veo el operador más. C ++ hace que las cosas que son caras o peligrosas sean más detalladas o difíciles de hacer, y me gusta de esta manera. Eche un vistazo a Java: uno de los peores errores de codificación es el abuso del operador plus. Por supuesto, de nuevo, C ++ no siempre facilita las cosas baratas y rápidas, pero bueno. Los buenos programadores de C ++ son muy conscientes del rendimiento. ;)
wilhelmtell
2
Estoy de acuerdo con los dos en que op+()no debería definirse en absoluto debido a su ambigüedad. Pero los vectores suelen ser parte de un espacio vectorial (matemático) y existe una definición canónica de sumar dos vectores y multiplicación escalar. Para llevar su argumento más allá: un simple doublees también un vector, por lo que si agrega dos doublevariables como a+b, esperaría obtener una nueva doubley no una pairdoble como (a,b). Multiplicar con un escalar también es canónico, pero multiplicar dos vectores no lo es. Así que la sobrecarga debe hacerse con cuidado ..
Dane
1

Inserte un nuevo elemento y devuélvalo, útil para semánticas de movimientos simples como push_back(c).swap(value)y casos relacionados.

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();
}

Pop y devuelve un artículo:

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;
}
Roger Pate
fuente
1

En mi opinión, debe haber más funcionalidad para 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_
rlbond
fuente
1
¿Por qué no mover la PairTypeplantilla a operator ()? Además, los guiones bajos dobles en el identificador están reservados.
GManNickG
@GMan - Porque entonces no puedes usar unary_function, lo que necesito en algún momento de mi código. En cuanto a los subrayados dobles, gracias por hacérmelo saber, tendré que cambiar eso.
rlbond
Esto usa nombres dependientes (argument_type, result_type) incorrectamente y se requiere que los compiladores lo rechacen. "En la definición de una plantilla de clase o un miembro de una plantilla de clase, si una clase base de la plantilla de clase depende de un parámetro de plantilla, el alcance de la clase base no se examina durante la búsqueda de nombres no calificados ni en el punto de definición del plantilla o miembro de clase o durante una instanciación de la plantilla o miembro de clase ". [14.6.2 / 3, C ++ 03]
@Roger Pate: No conocía esa regla. Está arreglado ahora.
rlbond
1
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

Si necesita usar muchas funciones que toman puntero + número de bytes, siempre es solo

fun(vec.data(), bytesize(vec));
hrehfeld
fuente
1

Duplica una cadena con *:

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();
}
kirbyfan64sos
fuente
0

Uno de mis favoritos es el Transposerque encuentra una transposición de una tupla de contenedores del mismo tamaño. Es decir, si tiene un tuple<vector<int>,vector<float>>, lo convierte en un vector<tuple<int, float>>. Resulta útil en la programación XML. Así es como lo hice.

#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;
}
Sumant
fuente
0

No estoy seguro si estos califican como envoltorios estándar, pero mis funciones de ayuda de uso común son:

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 y V aquí son argumentos de plantilla. La última función funciona de la misma manera que [] -operator, pero con el cambio de tamaño automático para adaptarse al índice necesario.

AareP
fuente
1
El nombre 'aserción' está reservado (en todos los ámbitos) por la biblioteca estándar para su macro de ese nombre.
1
Creo que también hay una macro assert () declarado en encabezados de windows o mfc. Ambos fallan en el evento WM_PAINT, porque mostrar el diálogo de aserción desencadena la siguiente evaluación en algunos casos. Entonces, al final, no es un gran problema reemplazar esas implementaciones con errores por una tercera. Todo lo que tienes que hacer es volver a declarar explícitamente tu propia macro de aserción después de #include <assert>, o simplemente usar #undef assert.
AareP
0

De manera similar a lo que la gente publicó antes, tengo sobrecargas de algoritmos de conveniencia para simplificar el paso de argumentos de iterador. Yo llamo algoritmos como este:

for_each(iseq(vec), do_it());

Sobrecargué todos los algoritmos de modo que tomen un solo parámetro de tipo en input_sequence_range<>lugar de los dos iteradores de entrada (entrada como en cualquier cosa que no sea mera salida).

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)
    { }
};

Y así es como iseq()funciona:

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());
}

Del mismo modo, tengo especializaciones para

  • const_iterators
  • punteros (matrices primitivas)
  • iteradores de flujo
  • cualquier rango [inicio, fin) solo para un uso uniforme: use iseq () para todo
Wilhelmtell
fuente
3
... o simplemente puede usar Boost.Range y obtener los beneficios de los adaptadores de rango y el código ampliamente probado y revisado por pares.
Mankarse
0

Borrado desordenado para std::vector. La forma más eficaz de borrar un elemento de a, vectorpero no conserva el orden de los elementos. No vi el sentido de extenderlo a otros contenedores, ya que la mayoría no tiene la misma penalización por eliminar elementos del medio. Es similar a algunas otras plantillas ya publicadas, pero se usa std::swappara mover elementos en lugar de copiar.

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 devuelve el signo de un tipo. Devuelve -1negativo, 0cero y 1positivo.

template <typename T>
int signum(T val)
{
    return (val > T(0)) - (val < T(0));
}

La abrazadera se explica por sí misma, fija un valor para que se encuentre dentro del rango dado. Se perturba mi mente que la biblioteca estándar incluye miny maxaunque noclamp

template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}
Fibbles
fuente