71 votes

Quelles fonctions wrapper de la bibliothèque standard C++ utilisez-vous ?

Cette question La question que j'ai posée ce matin m'a amené à me demander quelles sont les fonctionnalités qui, selon vous, manquent à la bibliothèque standard C++ et comment vous avez procédé pour combler les lacunes avec des fonctions d'habillage. Par exemple, ma propre bibliothèque d'utilitaires possède cette fonction pour le vector append :

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

et celui-ci pour effacer (plus ou moins) tout type - particulièrement utile pour des choses comme std::stack :

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

J'en ai quelques autres, mais je suis intéressé par ceux que vous utilisez ? Veuillez limiter vos réponses à emballage c'est-à-dire pas plus de quelques lignes de code.

32voto

Viktor Sehr Points 5634

boost: : tableau

contient (conteneur, val) (assez simple, mais pratique).

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(begin, end, value)

Une version plus rapide de std::remove avec l'exception qu'elle ne préserve pas l'ordre des objets restants.

template <typename T> 
T remove_unstable(T start, T stop, const typename T::value_type& val){  
    while(start != stop) {      
        if (*start == val) {            
            --stop;             
            ::std::swap(*start, *stop);         
        } else {            
            ++start;        
        }   
    }   
    return stop; 
}

(dans le cas d'un vecteur de types pod (int, float etc) et que presque tous les objets sont supprimés, std::remove pourrait être plus rapide).

28voto

sbk Points 4987

Très souvent, j'utilise le vecteur comme un ensemble d'éléments sans ordre particulier (et, évidemment, lorsque je n'ai pas besoin de vérifications rapides de type "is-this-element-in-the-set"). Dans ces cas, appeler erase() est une perte de temps car cela réordonnera les éléments et je ne me soucie pas de l'ordre. C'est là que la fonction O(1) ci-dessous est utile - il suffit de déplacer le dernier élément à la position de celui que vous voulez effacer :

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}

22voto

sbi Points 100828
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, comme il semble que ce n'est pas aussi simple que je le pensais, voici une explication :
Dans son constructeur temp_value stocke une référence à une variable et une copie de la valeur originale de la variable. Dans son destructeur, il restaure la variable référencée à sa valeur d'origine. Ainsi, peu importe ce que vous avez fait à la variable entre sa construction et sa destruction, elle sera réinitialisée lorsque l'objet temp_value sort du champ d'application.
Utilisez-le comme ça :

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
}

Voici une autre approche qui utilise l'astuce du garde-barrière :

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

C'est juste un peu plus de code passe-partout, mais cela permet une utilisation plus propre :

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

Cela lui enlève cependant la possibilité d'être utilisé dans une classe.


Voici une troisième façon d'obtenir le même effet (qui ne souffre pas des problèmes liés à la possibilité de lancer des destructeurs) :

Mise en œuvre :

//none -- it is built into the language

Utilisation :

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

21voto

Glen Points 13521

Ce n'est pas vraiment un emballage, mais le fameux manquant. copy_if . De ici

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

18voto

sbi Points 100828
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;}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X