Których funkcji opakowujących biblioteki standardowej języka C ++ używasz?

81

To pytanie , zadane dziś rano, sprawiło, że zacząłem się zastanawiać, których funkcji, Twoim zdaniem, brakuje w bibliotece standardowej C ++ i jak wypełniłeś luki funkcjami opakowującymi. Na przykład moja własna biblioteka narzędzi ma tę funkcję dodawania wektorów:

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

a ten do czyszczenia (mniej więcej) dowolnego typu - szczególnie przydatny do rzeczy takich jak std :: stack:

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

Mam jeszcze kilka, ale interesuje mnie, których używasz? Ogranicz odpowiedzi do funkcji opakowujących - tj. Nie więcej niż kilka linii kodu.

anon
źródło
9
Czy liczy się to, że zawinąłem większość algorytmu STL, aby działał na całych kontenerach zamiast na zakresach, tylko dlatego, że majstrowanie przy iteratorach jest tak częstym błędem :)?
Matthieu M.
2
@Billy właściwie CW nie jest wymówką do zadawania subiektywnych pytań. Zmienię tytuł, co powinno uszczęśliwić ludzi.
4
@kts: Ponieważ vector :: insert ma iteratory o swobodnym dostępie, dobra implementacja będzie używać wysyłania w czasie kompilacji, aby zrobić to samodzielnie.
4
Czy nie lepiej napisać, c.swap(C())żeby wyczyścić pojemnik?
Alexandre C.
5
@Alexandre: To niedozwolone: ​​wiąże tymczasowy z niebędącym stałym ref. C (). Swap (c) zadziała.

Odpowiedzi:

37

boost :: array

zawiera (pojemnik, wartość) (dość proste, ale wygodne).

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 (początek, koniec, wartość)

Szybsza wersja std :: remove z tym wyjątkiem, że nie zachowuje kolejności pozostałych obiektów.

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

(w przypadku wektora typów podów (int, float itp.) i prawie wszystkie obiekty są usuwane, std :: remove może być szybsze).

Viktor Sehr
źródło
4
Czy ktoś jeszcze uważa, że ​​zawiera, potrzebuje trzeciego szablonu ( bool sorted=false) i specjalizacji, kiedy sorted==truewywołać binary_searchzamiast find?
KitsuneYMG
6
@kts: Gdy wiesz, że kontener jest posortowany, po prostu zadzwoń bezpośrednio do binary_search .
2
Odpowiednik boost :: array STL jest dostępny w przestrzeni nazw tr1 w najnowszych kompilatorach (nawet w kodowaniu): std :: tr1 :: array <>
Klaim
36

Dość często używałbym wektora jako zestawu elementów w żadnej określonej kolejności (i oczywiście, gdy nie potrzebuję szybkich sprawdzeń tego elementu w zestawie). W takich przypadkach wywołanie erase () jest stratą czasu, ponieważ zmieni kolejność elementów i nie obchodzi mnie kolejność. Wtedy przydaje się poniższa funkcja O (1) - po prostu przesuń ostatni element na pozycję tego, który chcesz usunąć:

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}
sbk
źródło
Dobry. Nie myślałem o zrobieniu tego w ten sposób ... :) Powinien istnieć opakowanie 'Bag' (podobne do stosu i kolejki), które przyspiesza te operacje, gdy kolejność nie jest ważna.
Macke
12
A w C ++ 0x v[index] = st::move(v.back()); v.pop_back();jest tak wydajne, jak to tylko możliwe.
GManNickG,
@Matthieu: Spójrz na znacznik czasu na nich. : PI zauważyłem kilka godzin później, daleko poza dozwolonym czasem edycji.
GManNickG
@GMan: czy jesteś tego pewien? wygląda dla mnie na to, że v.pop_back () może spowodować niezdefiniowane zachowanie, gdy zostanie wywołany destruktor.
Viktor Sehr
1
@Viktor: Semantyka ruchu nie oznacza „Zabieram twoje zasoby i to wszystko”, mają na myśli „Zabieram twoje zasoby i stawiam cię w stanie braku zasobów”. Innymi słowy, semantyka przenoszenia musi zapewniać bezpieczne zniszczenie obiektu; całkiem łatwe do zrobienia, po prostu ustaw wskaźniki na null.
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, ponieważ wydaje się, że nie jest to tak proste, jak myślałem, oto wyjaśnienie: w
swoim konstruktorze temp_valueprzechowuje odniesienie do zmiennej i kopię oryginalnej wartości zmiennej. W swoim destruktorze przywraca zmienną, do której odwołuje się odwołanie, jej pierwotną wartość. Tak więc, bez względu na to, co zrobiłeś ze zmienną między budową a zniszczeniem, zostanie zresetowana, gdy temp_valueobiekt wyjdzie poza zakres.
Użyj tego w ten sposób:

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
}

Oto inne podejście, które wykorzystuje sztuczkę z ochroną zasięgu:

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

To tylko trochę więcej kodu źródłowego, ale pozwala na czystsze użycie:

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

Jednak usuwa jego zdolność do użycia w klasie.


Oto trzeci sposób na osiągnięcie tego samego efektu (który nie cierpi z powodu problemów związanych z potencjalnym rzucaniem destruktorów):

Realizacja:

//none -- it is built into the language

Stosowanie:

#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
źródło
1
@Billy: Służy do późniejszego automatycznego przywracania wartości. (I val powinien zostać wyeliminowany z ctora.)
Przepraszam, wciąż się zgubiłem (jestem nowy w C ++), czy ktoś jest w stanie to zmylić?
dreamlax
1
@dreamlax: Do odpowiedzi dodałem opisowy tekst z opisem. Czy jest to teraz zrozumiałe, czy też powinienem zagłębić się w szczegóły?
sbi
1
och, drugi jest całkiem sprytny.
jalf
1
Hmm, jaki jest prawdziwy przypadek użycia tego?
paulm
22

Nie jest to opakowanie, ale niesławny brak copy_if. Od tutaj

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
źródło
2
Nie odpowiada na pytanie, nie jest opakowaniem standardowej biblioteki.
10
@Roger Pate, tak wiem, dlatego odpowiedź zaczyna się od słów „Nie jest to opakowanie, ale…”.
Glen
1
Szczegóły implementacji @Roger. Jeśli naprawdę chcesz, możesz to zaimplementować w zakresie 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
źródło
2
Te też mam. :) +1 Do tego ile jest warte, potrzebujesz tylko dwóch (porzuć wersje const). Gdy tablica jest stała, Tbędzie const Ui otrzymasz zamierzoną funkcję.
GManNickG,
@GMan: Była jakaś wersja GCC, która nie kompilowała jakiegoś kodu tylko z nie- constwersjami, dlatego te constwersje tam są. Ponieważ mógł to być błąd tej konkretnej wersji GCC, usunę je.
sbi
1
@Marcus: Są znacznie starsze niż Boost.Range. :)
sbi
4
@Roger: opakowuje tablice, aby mogły być używane ze standardową biblioteką. Proszę bardzo. :)
sbi
2
@Stacked, @sbe: tablice nigdy nie są przekazywane przez wartość, niezależnie od tego, czy istnieje &. &Ma umożliwić typu odliczenie długości tablicy.
Mankarse
12

Czasami czuję, że jestem w begin()i end()piekło. Chciałbym mieć kilka funkcji, takich jak:

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

Te i inne podobne do std::find, std::for_eachiw zasadzie wszystkich algorytmów STL.

Czuję, że sort(x)jest to znacznie szybsze do przeczytania / zrozumienia niż sort(x.begin(), x.end()).

Odwrotność
źródło
1
Wskazówka; zamiast tego użyj ´sort (boost :: begin (x), boost: end (x)); ´ i możesz również sortować tablice.
Viktor Sehr
4
Boost.Range v2 ma takie adaptery dla całej biblioteki standardowej.
ildjarn
9

Nie używam już tego prawie tak często, ale kiedyś był podstawą:

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

Będzie aktualizować o więcej, gdy je zapamiętam. : P

Jon Purdy
źródło
3
Hehe - rodzaj skrótu do boost::lexical_cast<t, t>.
Billy ONeal
1
tak, to jest świetne, jeśli nie chcesz zwiększać projektu
Steve
11
@BillyONeal: Dlatego już go nie używam. @Steve: Dlatego nadal go używam.
Jon Purdy,
Wywołanie tego na a char*lub a byłoby niepotrzebnie kosztowne std::string. Może potrzebna jest specjalizacja szablonowa?
wilhelmtell
O ile dobrze pamiętam, boost::lexical_castposiada kilka takich specjalizacji i sprawdzania błędów. Jednak aby określić liczbę nieparzystą, działa to dobrze.
Jon Purdy,
9

Funkcja narzędziowa w zestawie narzędzi Everyones jest oczywiście copy_if . Nie jest to jednak opakowanie.

Innym pomocnikiem, którego często używam deleter, jest funktor, z którym korzystamstd::for_each do usuwania wszystkich wskaźników w kontenerze.

[edytuj] Przeglądając moje „sth.h” również znalazłem vector<wstring> StringSplit(wstring const&, wchar_t);

MSalters
źródło
+1 dla funktorów usuwających. Mój funktor deleter działa dobrze z większością kontenerów, jednak bawiłem się z jego działaniem z std :: map, gdzie klucz lub wartość jest wskaźnikiem. Próbowałem użyć cech typu, aby rozwiązać problem, ale tak naprawdę nie zaszedłem zbyt daleko ze względu na ograniczenia czasowe. Czy rozwiązałeś ten problem, czy uważasz, że jest to problem?
Glen
2
@Matthieu M. niestety nie każdy z nas może korzystać ze wspomagania.
Glen
@Glen: Wszystkie te problemy można rozwiązać za pomocą inteligentnych wskaźników, najlepiej (ale niekoniecznie) z boostu. Ważnym efektem ubocznym jest to, że kontenery ze wskaźnikami do dynamicznie przydzielanych obiektów również nagle stają się bezpieczne pod względem wyjątków.
sbi
@Glen tutaj, tutaj dla projektów, które nie przewidują bibliotek innych niż STD, aż do Boost lub TR1 włącznie.
pszenicy
7
@nieszczęśliwe ofiary korporacyjnej głupoty: uzyskaj wyjątek dla wewnętrznych bibliotek, a następnie zaimportuj przydatne części Boost do wewnętrznej biblioteki. Nawet w polityce wszystkie problemy można rozwiązać na innym poziomie pośrednictwa.
MSalters
9

Mam nagłówek, który umieszcza następujące elementy w przestrzeni nazw „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 Teran
źródło
2
To split()pochłania wszelkie błędy występujące w programie std::getline(), po cichu zwracając wektor, który jest zbyt krótki.
sbi
oczywiście powinieneś sprawdzić size()wynik przed pobraniem ciągów.
Evan Teran
A skąd mam wiedzieć, ile łańcuchów powinien mieć wynik?
sbi
2
@sbi: Twój komentarz wzbudził moje zainteresowanie tym, co faktycznie może pójść nie tak (poza ciągiem znaków, które po prostu nie ma wystarczającej liczby tokenów do uzyskania) w pętli stringstream/ getline. Zadałem tutaj pytanie na ten temat: stackoverflow.com/questions/2562906/…
Evan Teran
2
@Evan: Poprawiono mnie. Zobacz mój komentarz na stackoverflow.com/2563542#2563542 . Przepraszam.
sbi
8

Niesławnie brakujący erasealgorytm:

  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.
źródło
+1, miał zamieścić mój dokładny odpowiednik. Chociaż nazwałem to remove_erase (...)
Viktor Sehr
2
Jedynym problemem jest to, że łamie semantyczny idiom wymazywania-usuwania oczekiwany w STL. Potrzebujesz idiomu kasuj-usuń z dowolnym algorytmem, który ma semantykę usuwania - nie tylko std::remove. Na przykład std::unique.
Billy ONeal
No cóż, mam do tego kontenery przystosowane do większości algorytmów STL :) Ale to jest ten, którego używam najczęściej, zwykle jeśli chcę unikalności, setna początek używam a .
Matthieu M.
@Matthieu M .: Pamiętaj tylko, że robiąc to, będziesz mieć ludzi, którzy pracują z STL przez cały czas, mając dzwonki alarmowe w swoich głowach "OSTRZEŻENIE: ALGORYTM USUWANIA PODOBNY BEZ WEZWANIA DO SKASOWANIA !!" . Naprawdę nie ma w tym nic złego, ale nie jest to coś, co zrobiłbym, gdybym musiał udostępnić swój kod wielu programistom. Tylko moje 2 centy.
Billy ONeal
1
@Billy: dlatego nazwałem to wymazywaniem, a nie usuwaniem. Poza tym niewiele mogę zrobić poza tym, że pozwolę im zapoznać się z kodem. Na szczęście dzięki nowoczesnemu IDE definicja jest w zasięgu jednego kliknięcia :)
Matthieu M.
7

Zawijanie 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

Celem jest oddzielenie formatowania od wyjścia bez wpadania w kłopoty ze sprintf i podobnymi. To nie jest ładne, ale jest bardzo przydatne, zwłaszcza jeśli twoje wytyczne dotyczące kodowania zakazują iostreams.


Oto wersja, która przydziela w razie potrzeby, od Neila Butterwortha. [Zobacz historię zmian wersji Mike'a, którą usunąłem jako podzbiór pozostałych dwóch. Jest podobny do Neila, z wyjątkiem tego, że ten ostatni jest bezpieczny dla wyjątków, używając vector zamiast delete []: ctor łańcucha zgłosi błąd przy alokacji. Mike używa również tej samej techniki, którą pokazano później, aby określić rozmiar z przodu. –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] );
}

Oto wersja, która określa wymagany rozmiar z góry, od Rogera Pate . Wymaga to zapisywalnych ciągów std ::, które są dostarczane przez popularne implementacje, ale są wyraźnie wymagane przez C ++ 0x. [Zobacz historię zmian wersji Marcusa, którą usunąłem, ponieważ jest nieco inna, ale zasadniczo stanowi podzbiór poniższych. –RP]

Realizacja

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

Interfejs publiczny

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
źródło
@Neil: Od man vsnprintf: „Te funkcje zwracają liczbę znaków płyt ... lub wartość ujemną w przypadku wystąpienia błędu wyjście, z wyjątkiem dla snprintf()a vsnprintf(), która zwraca liczbę znaków, które zostały wydrukowane, gdy n były nieograniczone ... „Stąd fałszywe wywołanie z buforem 0 w celu zmierzenia wymaganego rozmiaru bufora.
Mike DeSimone
@Checkers: Ah, Boost. Tak duży potencjał, że mnie też nie pozwolą. Mam nadzieję, że kiedyś. W każdym razie, czy Boost stał się na tyle duży, że nie można go jeszcze w pełni zrozumieć? Byłbym szczęśliwy, gdybym to dostał boost::spirit.
Mike DeSimone
W rzeczywistości jest to kod Windows - z MSDN „W przypadku _vsnprintf, jeśli liczba bajtów do zapisu przekracza bufor, zapisywana jest liczba bajtów i zwracana jest wartość –1”. ale przenoszę to na Linuksa. Nie pamiętam, czy moja aplikacja na Linuksa faktycznie tego używa, czy też przetestowałem ją tam pod kątem dużych rozmiarów buforów - muszę to zrobić. Dzięki.
1
jeśli mimo wszystko używasz kodu Windows, śmiało użyj, _vscprintfaby określić wymagany rozmiar bufora.
smerlin
6

is_sortedNarzędzie do pojemników do testów przed zastosowaniem algorytmów jak includektóre oczekują posortowaną wpis:

  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

Tak, wiem, lepiej byłoby zanegować predykat i użyć predykatowej wersji adjacent_find:)

Matthieu M.
źródło
1
pod warunkiem, że wykonasz test tylko w assert(): p
wilhelmtell
Nie powinieneś używać notacji węgierskiej.
the_drow
3
@the_drow: bardzo dziękuję za ten pomocny komentarz :) Nie jestem tym fanem, ale to wymóg, w którym pracuję ... Od tego czasu otrząsnąłem się z nałogu, nie martw się o moją duszę;)
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
źródło
Wolę korzystać z Boost.Bimapbiblioteki (lub Boost.MultiIndexw bardziej złożonych sytuacjach)
Matthieu M.
1
Nie rozumiem, dlaczego assert ()?
Viktor Sehr
@Viktor: aby upewnić się, że nie ma zduplikowanych kluczy w reverse_map. Rozważ, że mapma (1 -> "jeden"; 2 -> "jeden") reverse_mapdostanie jeden element ("jeden" -> 1). Assert to złapie. Zobacz także: bijection
sbk
3
Btw, sbi, posiadanie kodu z efektami ubocznymi w funkcji assert () bardzo cię ugryzie, gdy kompilujesz z NDEBUG, a assert () zostaną całkowicie usunięte.
sbk
2
gah, po aktualizacji mój pierwszy komentarz wygląda naprawdę głupio, stackoverflow jest numerem 1 podczas wyszukiwania w Google mojego nazwiska, więc mam nadzieję, że żaden przyszły pracodawca tego nie zobaczy =)
Viktor Sehr
3

Patrząc na moje stl_util.h, wiele klasyków (funkcje usuwające copy_if), a także tę (prawdopodobnie również dość powszechną, ale nie widzę tego w dotychczasowych odpowiedziach) do przeszukiwania mapy i zwracania znalezionej wartości lub domyślny, ala getw Pythonie 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;
   }

Używanie domyślnych null_resultwartości domyślnych Vjest takie samo jak zachowanie std::map's operator[], ale jest to przydatne, gdy mapa jest stała (często dla mnie) lub jeśli domyślne V nie jest właściwe do użycia.

Jack Lloyd
źródło
A co jeśli V jest int, float lub jakimś innym prymitywem?
Odwrócenie
Pusta inicjalizacja wartości działa dla podstawowych typów w C ++. W przypadku liczb całkowitych i zmiennoprzecinkowych wartość domyślna wynikałaby z wartości null_result 0.
Jack Lloyd
3

Oto mój zestaw dodatkowych narzędzi, zbudowanych na podstawie wrappera boost.range'ish std-algo, którego możesz potrzebować do niektórych funkcji. (to trywialne do napisania, to jest interesująca rzecz)

#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
źródło
+1 dla for_each_permutation (...) , głównie dlatego, że napisałem podobny wrapper =). Ale dlaczego erase_duplicates (...) zwraca podpisaną int?
Viktor Sehr
Cześć Viktor! Myślę, że powinieneś był zobaczyć for_each_permutation w swojej poprzedniej pracy. ;) erase_duplicates zwraca liczbę skasowanych elementów, co jest przydatne do logowania i debugowania.
Macke
Hmm mógł go przejrzeć, nie zdając sobie sprawy z tego, co robi ;) , w każdym razie rozumiem, dlaczego zwraca liczbę całkowitą, po prostu nie rozumiem, dlaczego liczba całkowita jest podpisana (a dokładniej; dlaczego nie jest bez znaku )?
Viktor Sehr
Ach. Po prostu lenistwo z mojej strony :-P. size_t to odpowiedni typ.
Macke
3

Wydaje mi się, że potrzebuję iloczynu kartezjańskiego, na przykład {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
źródło
Zauważ, że InIt2 musi być iteratorem do przodu zamiast iteratorem wejściowym. Iteratory wejściowe nie są odpowiednie dla wielu przebiegów.
2

Nazwałbym taką funkcję dołączającą nazwą i użyłbym operatora + =, operator * = i tak dalej dla operacji elementarnych, takich jak:

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

kilka innych prostych i oczywistych opakowań, jak sugerowano wcześniej:

    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);
    }
Duńczyk
źródło
7
Te operatory są bardzo dobrym przykładem, dlaczego przeciążanie operatorów powinno być wykonywane rzadko. Chciałbym mieć myśl, vec+=valby dołączyć wartości do wektora. (Zobacz stackoverflow.com/questions/2551775/. ) Teraz, kiedy zobaczyłem twoją implementację, myślę, że jest to równie właściwa interpretacja znaczenia +=. Nie wiedziałbym, który z nich byłby dobry lub zły, więc prawdopodobnie równie dobrze, że nie mamy +=do tego std::vector.
sbi
1
@sbi Zgadzam się. Uważam operator+()brakuje niesamowite wczesny wgląd standardu. Zwykle oczekuję operacji O (1) wszędzie tam, gdzie widzę operator plus. C ++ sprawia, że ​​rzeczy, które są drogie lub niebezpieczne, stają się bardziej rozwlekłe lub trudne do wykonania i lubię to w ten sposób. Spójrz na Javę: jednym z najgorszych błędów w kodowaniu jest nadużycie operatora plus. Oczywiście C ++ nie zawsze sprawia, że ​​tanie i szybkie rzeczy są łatwe, ale hej. Dobrzy programiści C ++ są bardzo świadomi wydajności. ;)
wilhelmtell
2
Zgadzam się z Wami obojgiem, że op+()nie powinno się tego w ogóle definiować ze względu na jego niejednoznaczność. Ale wektory są zwykle częścią (matematycznej) przestrzeni wektorowej i istnieje kanoniczna definicja dodawania dwóch wektorów i mnożenia przez skalar. Idąc dalej: proste doublejest również wektorem, więc jeśli dodasz dwie doublezmienne, tak jak a+bwtedy, spodziewasz się, że otrzymasz nową, doublea nie pairpodwójną (a,b). Mnożenie przez skalar również jest kanoniczne, ale mnożenie dwóch wektorów już nie. Więc przeciążanie powinno być wykonywane ostrożnie ..
Dane,
1

Wstaw nowy element i zwróć go, co jest przydatne w przypadku prostej semantyki przenoszenia, takiej jak push_back(c).swap(value)i podobne przypadki.

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 i zwróć przedmiot:

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
źródło
1

IMO musi być więcej funkcji dla 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
źródło
1
Dlaczego nie przenieść PairTypeszablonu do operatora ()? Również podwójne podkreślenia w identyfikatorze są zastrzeżone.
GManNickG
@GMan - Ponieważ wtedy nie możesz użyć unary_function, czego potrzebuję w pewnym momencie mojego kodu. Jeśli chodzi o podwójne podkreślenia, dziękuję za poinformowanie mnie - muszę to zmienić.
rlbond
Powoduje to niepoprawne użycie nazw zależnych (argument_type, result_type), a kompilatory są zobowiązane do odrzucenia go. „W definicji szablonu klasy lub elementu członkowskiego szablonu klasy, jeśli klasa bazowa szablonu klasy zależy od parametru szablonu, zakres klasy bazowej nie jest badany podczas niekwalifikowanego wyszukiwania nazwy ani w punkcie definicji szablon lub element członkowski klasy albo podczas tworzenia wystąpienia szablonu lub elementu członkowskiego klasy. " [14.6.2 / 3, C ++ 03]
@Roger Pate: Nie wiedziałem o tej zasadzie. To jest teraz naprawione.
rlbond
1
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

Jeśli potrzebujesz użyć wielu funkcji, które pobierają wskaźnik + liczbę bajtów, zawsze jest to po prostu

fun(vec.data(), bytesize(vec));
hrehfeld
źródło
1

Zduplikuj ciąg z *:

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
źródło
0

Jednym z moich ulubionych jest ten, Transposerktóry znajduje transpozycję krotki pojemników o tym samym rozmiarze. Oznacza to, że jeśli masz plik tuple<vector<int>,vector<float>>, przekształca go w plik vector<tuple<int, float>>. Przydaje się w programowaniu XML. Oto jak to zrobiłem.

#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
źródło
0

Nie jestem pewien, czy kwalifikują się one jako otoki std, ale moje często używane funkcje pomocnicze to:

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 i V są tutaj argumentami szablonowymi. Ostatnia funkcja działa tak samo jak [] -operator, ale z automatyczną zmianą rozmiaru w celu dopasowania do potrzebnego indeksu.

AareP
źródło
1
Nazwa „assert” jest zarezerwowana (we wszystkich zakresach) przez bibliotekę standardową dla jej makra o tej nazwie.
1
Myślę, że istnieje również makro assert () zadeklarowane w oknach lub w nagłówkach mfc. Oba kończą się niepowodzeniem w zdarzeniu WM_PAINT, ponieważ wyświetlenie okna dialogowego asercji wyzwala w niektórych przypadkach kolejną asesję. Więc ostatecznie zastąpienie tych wadliwych implementacji trzecią nie jest wielką sprawą. Wszystko, co musisz zrobić, to jawnie ponownie zadeklarować własne makro assert po #include <assert> lub po prostu użyć #undef assert.
AareP
0

Podobnie do tego, co ludzie publikowali wcześniej, mam mnóstwo wygodnych algorytmów upraszczających przekazywanie argumentów iteratora. Nazywam algorytmy w ten sposób:

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

Przeciążałem wszystkie algorytmy w taki sposób, że pobierają pojedynczy parametr typu input_sequence_range<>zamiast dwóch iteratorów wejściowych (dane wejściowe jak w przypadku wszystkiego, co nie jest zwykłym wyjściem).

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

A tak to iseq()działa:

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

Podobnie mam specjalizacje

  • const_iterators
  • wskaźniki (tablice pierwotne)
  • iteratory strumienia
  • dowolny zakres [początek, koniec) tylko do jednolitego użytku: użyj iseq () do wszystkiego
wilhelmtell
źródło
3
... lub możesz po prostu użyć Boost.Range i czerpać korzyści z adapterów zakresu i recenzowanego, szeroko testowanego kodu.
Mankarse
0

Nieuporządkowane wymazywanie dla std::vector. Najbardziej efektywny sposób usunięcia elementu z elementu a, vectorale nie zachowuje kolejności elementów. Nie widziałem sensu rozszerzania go na inne pojemniki, ponieważ większość nie ma takiej samej kary za usuwanie przedmiotów ze środka. Jest podobny do innych już opublikowanych szablonów, ale służy std::swapdo przenoszenia elementów zamiast kopiowania.

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 zwraca znak typu. Zwraca wartość -1ujemną, 0zero i 1dodatnią.

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

Zacisk jest dość oczywisty, zaciska wartość tak, aby mieściła się w podanym zakresie. To miesza mi w głowie, że biblioteka standardowa zawiera mina maxjednak nieclamp

template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}
Fibbles
źródło