어떤 C ++ 표준 라이브러리 래퍼 함수를 ​​사용하십니까?


81

이 질문 은 오늘 아침에 C ++ 표준 라이브러리에서 누락되었다고 생각하는 기능과 래퍼 함수로 공백을 채우는 방법에 대해 궁금해했습니다. 예를 들어 내 자신의 유틸리티 라이브러리에는 벡터 추가를위한 다음 기능이 있습니다.

template <class T>
std::vector<T> & operator += ( std::vector<T> & v1,
                               const std::vector <T> & v2 ) {
    v1.insert( v1.end(), v2.begin(), v2.end() );
    return v1;
}

그리고 이것은 모든 유형을 (더 많거나 적게) 지우는 데 사용됩니다-특히 std :: stack과 같은 것에 유용합니다.

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

몇 가지 더 있지만 어떤 것을 사용하는지 관심이 있습니까? 래퍼 함수에 대한 답변을 제한하십시오. 즉, 코드 몇 줄 이하로 제한하십시오 .


9
반복자를 엉망으로 만드는 것이 너무 흔한 실수이기 때문에 범위 대신 전체 컨테이너에서 작동하도록 STL 알고리즘의 대부분을 래핑했다고 계산합니까?
Matthieu M.

2
@Billy 실제로 CW는 주관적인 질문에 대한 변명이 아닙니다. 사람들을 행복하게 해줄 제목을 바꿀 게요.

4
@kts : vector :: insert에는 랜덤 액세스 반복자가 제공되므로 좋은 구현은 컴파일 타임 디스패치를 ​​사용하여 자체적으로 수행합니다.

4
c.swap(C())컨테이너를 지우는 것이 더 낫지 않습니까?
Alexandre C.

5
@Alexandre : 허용되지 않습니다. 임시 참조를 상수가 아닌 참조에 바인딩합니다. C (). swap (c) 작동합니다.

답변:


37

boost :: 배열

contains (container, val) (매우 간단하지만 편리함).

template<typename C, typename T>
bool contains(const C& container, const T& val) {
   return std::find(std::begin(container), std::end(container), val) != std::end(container);
}

remove_unstable (시작, 끝, 값)

더 빠른 버전의 std :: remove는 나머지 개체의 순서를 유지하지 않는다는 점을 제외하고는 예외입니다.

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

(pod 유형 (int, float 등)의 벡터이고 거의 모든 객체가 제거 된 경우 std :: remove가 더 빠를 수 있습니다).


4
다른 사람이 포함하는 데 세 번째 템플릿 ( bool sorted=false)과 대신 sorted==true호출 할 때 전문화 가 필요하다고 생각합니까 ? binary_searchfind
KitsuneYMG

6
@kts : 컨테이너 가 정렬 된 것을 알고 있으면 binary_search를 직접 호출 하십시오 .

2
boost :: array STL은 최신 컴파일러 (
codewarrior 포함

36

꽤 자주 나는 벡터를 특별한 순서없이 항목의 집합으로 사용합니다 (그리고 빠른 is-this-element-in-the-set 검사가 필요하지 않을 때). 이 경우 erase ()를 호출하면 요소를 재정렬하고 순서에 신경 쓰지 않기 때문에 시간 낭비입니다. 바로 아래의 O (1) 함수가 유용 할 때입니다. 삭제하려는 요소의 위치로 마지막 요소를 이동하면됩니다.

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

잘 했어. 그렇게하는 것에 대해 생각하지 않았습니다 .. :) 주문이 중요하지 않을 때 이러한 작업의 속도를 높이는 'Bag'래퍼 (스택 및 대기열과 유사)가 있어야합니다.
Macke

12
그리고 C ++ 0x에서는 v[index] = st::move(v.back()); v.pop_back();최대한 효율적입니다.
GManNickG

@Matthieu : 타임 스탬프를보세요. : PI는 몇 시간 후 허용 된 편집 시간을 훨씬 지났습니다.
GManNickG 2010

@GMan : 확실합니까? v.pop_back ()이 소멸자가 호출 될 때 정의되지 않은 동작을 초래할 수있는 것처럼 보입니다.
Viktor Sehr

1
@Viktor : Move-semantics는 "나는 당신의 자원을 가져가는 것입니다"를 의미하는 것이 아니라 "나는 당신의 자원을 가져 가고 당신을 자원이없는 상태로 만든다"를 의미합니다. 즉, 이동 의미론은 객체가 안전하게 파괴 될 수 있는지 확인해야합니다. 매우 쉽게 할 수 있습니다. 포인터를 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&);
};

좋아, 이것이 내가 생각한 것만 큼 간단하지 않은 것 같기 때문에 여기에 설명이 있습니다.
생성자 temp_value에는 변수에 대한 참조와 변수의 원래 값의 복사본이 저장됩니다. 소멸자에서 참조 된 변수를 원래 값으로 복원합니다. 따라서 생성과 파괴 사이의 변수에 어떤 조치를 취하더라도 temp_value오브젝트가 범위를 벗어나면 재설정됩니다 .
다음과 같이 사용하십시오.

void f(some_type& var)
{
  temp_value<some_type> restorer(var); // remembers var's value

  // change var as you like
  g(var);

  // upon destruction restorer will restore var to its original value
}

다음은 범위 보호 트릭을 사용하는 또 다른 접근 방식입니다.

namespace detail
{
    // use scope-guard trick
    class restorer_base
    {
    public:
        // call to flag the value shouldn't
        // be restored at destruction
        void dismiss(void) const
        {
            mDismissed = true;
        }

    protected:
        // creation
        restorer_base(void) :
        mDismissed(false) 
        {}

        restorer_base(const restorer_base& pOther) :
        mDismissed(pOther.is_dismissed())
        {
            // take "ownership"
            pOther.dismiss();
        }

        ~restorer_base(void) {} // non-virtual

        // query
        bool is_dismissed(void) const
        {
            return mDismissed;
        }

    private:
        // not copy-assignable, copy-constructibility is ok
        restorer_base& operator=(const restorer_base&);

        mutable bool mDismissed;
    };

    // generic single-value restorer, could be made 
    // variadic to store and restore several variables
    template <typename T>
    class restorer_holder : public restorer_base
    {
    public:
        restorer_holder(T& pX) :
        mX(pX),
        mValue(pX)
        {}

        ~restorer_holder(void)
        {
            if (!is_dismissed())
                mX = mValue;
        }

    private:
        // not copy-assignable, copy-constructibility is ok
        restorer_holder& operator=(const restorer_holder&);

        T& mX;
        T mValue;
    };
}

// store references to generated holders
typedef const detail::restorer_base& restorer;

// generator (could also be made variadic)
template <typename T>
detail::restorer_holder<T> store(T& pX)
{
    return detail::restorer_holder<T>(pX);
}

약간 더 많은 상용구 코드이지만 더 깔끔하게 사용할 수 있습니다.

#include <iostream>

template <typename T>
void print(const T& pX)
{
    std::cout << pX << std::endl;
}

void foo(void)
{
    double d = 10.0;
    double e = 12.0;
    print(d); print(e);

    {
        restorer f = store(d);
        restorer g = store(e);

        d = -5.0;
        e = 3.1337;
        print(d); print(e);

        g.dismiss();
    }

    print(d); print(e);
}

int main(void)
{
    foo();

    int i = 5;
    print(i);

    {
        restorer r = store(i);

        i *= 123;
        print(i);
    }

    print(i);
}

하지만 클래스에서 사용할 수있는 능력을 제거합니다.


다음은 동일한 효과를 달성하는 세 번째 방법입니다 (소멸자를 던지는 문제를 겪지 않는).

이행:

//none -- it is built into the language

용법:

#include <iostream>

template <typename T>
void print(const T& pX)
{
    std::cout << pX << std::endl;
}

void foo(void)
{
    double d = 10.0;
    double e = 12.0;
    print(d); print(e);

    {
        double f(d);
        double g(e);

        f = -5.0;
        g = 3.1337;
        print(f); print(g);

        e = std::move(g);
    }

    print(d); print(e);
}

int main(void)
{
    foo();

    int i = 5;
    print(i);

    {
        int r(i);

        r *= 123;
        print(r);
    }

    print(i);
}

1
@Billy : 나중에 자동으로 값을 복원하기위한 것입니다. (그리고 val은 ctor에서 제거되어야합니다.)

죄송합니다. 아직 길을 잃었습니다 (C ++를 처음 접했습니다). 누구든지 바로 바보로 만들 수 있습니까?
dreamlax 2010

1
@dreamlax : 답변에 설명 텍스트를 추가했습니다. 지금 이해할 수 있습니까? 아니면 세부 사항을 더 자세히 살펴 봐야합니까?
sbi

1
오, 두 번째는 꽤 영리합니다.
jalf

1
이것에 대한 실제 사용 사례는 무엇입니까?
paulm

22

실제로 래퍼는 아니지만 악명 높은 copy_if. 에서 여기

template<typename In, typename Out, typename Pred>
Out copy_if(In first, In last, Out res, Pred Pr)
{
    while (first != last) {
        if (Pr(*first)) {
            *res++ = *first;
        }
        ++first;
    }
    return res;
}

2
stdlib에 대한 래퍼가 아니라 질문에 대답하지 않습니다.

10
@Roger Pate, 예, 그렇기 때문에 대답은 "정말 래퍼가 아니지만 ...."라는 단어로 시작됩니다.
Glen

1
@Roger 구현 세부 사항. 정말로 원한다면 remove_copy_if(). : p
wilhelmtell 2010

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

2
나도 가지고 있습니다. :) +1 가치있는 것을 위해, 당신은 단지 두 개만 필요합니다 (const 버전은 버리십시오). 배열이 CONST 때, T될 것입니다 const U그리고 당신은 의도 된 기능을 얻을.
GManNickG

@GMan : 오직 비와 일부 코드 컴파일하지 않을 GCC의 일부 버전이 있었다 const의이 왜 있는지, 버전을 const버전이는가. 특정 GCC 버전의 버그 일 수 있으므로 제거하겠습니다.
sbi

1
@Marcus : 이것들은 Boost.Range보다 훨씬 오래되었습니다. :)
sbi

4
@Roger : 표준 lib와 함께 사용할 배열을 래핑합니다. 됐습니다. :)
sbi 2010-06-02

2
@Stacked, @sbe : 배열은 .NET Framework가 있는지 여부에 관계없이 값으로 전달되지 않습니다 &. 는 &배열의 길이의 형태 추론이 가능하게하는 것이다.
Mankarse 2011-06-03

12

나는에있어 가끔은 기분 begin()end()지옥. 다음과 같은 기능을 갖고 싶습니다.

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

및에 대한 다른 유사한 것들 std::find,std::for_each 그리고 기본적으로 모든 STL 알고리즘.

나는 그것이 sort(x)보다 읽고 이해 하는 것이 훨씬 빠르다고 느낍니다 sort(x.begin(), x.end()).


1
힌트; 대신 ´sort (boost :: begin (x), boost : end (x)); ´를 사용하면 배열도 정렬 할 수 있습니다.
Viktor Sehr

4
Boost.Range v2에는 전체 표준 라이브러리에 대해 이와 같은 어댑터가 있습니다.
ildjarn

9

나는 더 이상 이것을 거의 사용하지 않지만 예전에는 스테이플이었습니다.

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

내가 기억하는대로 더 많이 업데이트됩니다. :피


3
Hehe-일종의 지름길 boost::lexical_cast<t, t>.
Billy ONeal

1
네, 프로젝트에 힘을 불어 넣고 싶지 않다면 아주 좋습니다
Steve

11
@BillyONeal : 그게 제가 더 이상 사용하지 않는 이유입니다. @Steve : 그것이 제가 아직도 그것을 사용하는 이유입니다.
Jon Purdy

char*또는에서 호출하는 것은 불필요하게 비쌉니다 std::string. 템플릿 전문화가 필요합니까?
wilhelmtell 2010

내가 올바르게 기억한다면, boost::lexical_cast그러한 전문화와 오류 검사가 많이 있습니다. 그러나 홀수를 문자열 화하려면 이것은 잘 작동합니다.
Jon Purdy

9

모든 사람의 도구 상자에있는 유틸리티 기능은 물론 copy_if 입니다. 그래도 실제로 래퍼는 아닙니다.

내가 일반적으로 사용하는 또 다른 도우미는 내가 deleter함께 사용하는 펑터입니다.std::for_each 는 컨테이너의 모든 포인터를 삭제 데 입니다.

나의 "sth.h"를 파헤치며 나는 또한 발견했다 vector<wstring> StringSplit(wstring const&, wchar_t);


삭제 자 펑터의 경우 +1. 내 deleter functor는 대부분의 컨테이너에서 잘 작동하지만 키나 값이 포인터 인 std :: map과 함께 작동하도록 해왔습니다. 문제를 해결하기 위해 유형 특성을 사용해 보았지만 시간 제약으로 인해 멀지 않았습니다. 이 문제를 해결 했습니까? 아니면 문제라고 생각하십니까?
Glen

2
@Matthieu M. 불행히도 우리 모두가 부스트를 사용할 수있는 것은 아닙니다.
Glen

@Glen : 이러한 모든 문제는 스마트 포인터를 사용하여 해결할 수 있습니다. 중요한 부작용으로 동적으로 할당 된 객체에 대한 포인터가있는 컨테이너도 갑자기 예외로부터 안전 해집니다.
sbi

@Glen은 여기에서 STD 외에 Boost 또는 TR1까지 포함하는 라이브러리를 규정하지 않는 프로젝트에 대해 설명합니다.
wheaties 2010

7
@ 기업 어리 석음의 불행한 희생자 : 사내 라이브러리에 대한 예외를 얻은 다음 Boost의 유용한 부분을 사내 라이브러리로 가져옵니다. 정치에서도 모든 문제는 다른 수준의 간접적으로 해결 될 수 있습니다.
MSalters

9

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

2
그건 split()제비 오류가 발생 std::getline()자동으로 너무 짧다 벡터를 반환.
sbi

물론 size()문자열을 검색하기 전에 결과를 확인해야 합니다.
Evan Teran

그리고 결과에 몇 개의 문자열이 있어야하는지 어떻게 알 수 있습니까?
sbi

2
@sbi : 귀하의 의견은 stringstream/ getline루프를 사용하여 실제로 잘못 될 수있는 사항 (문자열이 단순히 얻을 수있는 토큰이 충분하지 않은 것 제외)에 대한 내 관심을 정점으로했습니다 . 여기에 대한 질문이 있습니다. stackoverflow.com/questions/2562906/…
Evan Teran

2
@Evan : 나는 정정 당했다. stackoverflow.com/2563542#2563542 에서 내 의견을 참조하십시오 . 죄송합니다.
sbi

8

악명 높은 누락 erase알고리즘 :

  template <
    class Container,
    class Value
    >
  void erase(Container& ioContainer, Value const& iValue)
  {
    ioContainer.erase(
      std::remove(ioContainer.begin(),
                  ioContainer.end(),
                  iValue),
       ioContainer.end());
  } // erase

  template <
    class Container,
    class Pred
    >
  void erase_if(Container& ioContainer, Pred iPred)
  {
    ioContainer.erase(
      std::remove_if(ioContainer.begin(),
                     ioContainer.end(),
                     iPred),
       ioContainer.end());
  } // erase_if

+1, 내 정확한 등가물을 게시하려고했습니다. 내가 이름을 한 입력해도 그것은 remove_erase (...)
빅토르 SEHR

2
이것의 유일한 문제는 STL에서 예상되는 의미 삭제-제거 관용구를 깨는 것입니다. .NET 뿐만 아니라 의미 체계를 제거하는 모든 알고리즘 과 함께 erase-remove 관용구가 필요합니다 std::remove. 예 : std::unique.
Billy ONeal

글쎄, 나는 이것을 위해 대부분의 STL 알고리즘의 컨테이너 적응을 가지고 있습니다 :) 그러나 이것은 내가 가장 많이 사용하는 set것입니다.
Matthieu M.

@Matthieu M .:이 작업을 수행하면 항상 STL과 함께 일하는 사람들이 머릿속에 알람 벨이 울리게 될 것임을 명심하십시오. "경고 : 지울 호출없이 동일한 알고리즘 제거 !!" . 실제로 문제는 없지만 많은 프로그래머들 사이에서 내 코드를 공유해야한다면 내가 할 일이 아닙니다. 내 2 센트.
Billy ONeal

1
@Billy : 그래서 그것을 지우지 않고 지우라고 부르는 것입니다. 코드를 참조하는 것 외에는 할 수있는 일이별로 없습니다. 다행히 현대 IDE와 정의 : 한 번의 클릭으로 떨어져
마티유 M.

7

스프린트 래핑

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

목표는 sprintf 및 ilk 에 문제를 일으키지 않고 출력에서 ​​서식을 분리하는 것입니다 . 예쁘지는 않지만 특히 코딩 지침이 iostreams를 금지하는 경우 매우 유용합니다.


Neil Butterworth에서 필요에 따라 할당하는 버전이 있습니다. [마이크의 버전에 대한 업데이트 내역을 확인합니다. 나머지 두 버전에서 제거했습니다. Neil과 비슷하지만 후자는 delete [] 대신 vector를 사용하여 예외로부터 안전합니다. 문자열의 ctor는 할당 실패시 발생합니다. Mike 's는 또한 나중에 표시된 동일한 기술을 사용하여 크기를 미리 결정합니다. –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] );
}

Roger Pate 에서 필요한 크기를 미리 결정하는 버전이 있습니다. . 이를 위해서는 쓰기 가능한 std :: strings가 필요합니다. 이는 널리 사용되는 구현에서 제공되지만 C ++ 0x에서 명시 적으로 필요합니다. [Marcus '버전에 대한 개정 내역보기. 약간 다르지만 본질적으로 아래의 하위 집합이므로 제거했습니다. –RP]

이행

void vinsertf(std::string& s, std::string::iterator it,
             char const* fmt, int const chars_needed, va_list args
) {
  using namespace std;
  int err; // local error code
  if (chars_needed < 0) err = errno;
  else {
    string::size_type const off = it - s.begin(); // save iterator offset
    if (it == s.end()) { // append to the end
      s.resize(s.size() + chars_needed + 1); // resize, allow snprintf's null
      it = s.begin() + off; // iterator was invalidated
      err = vsnprintf(&*it, chars_needed + 1, fmt, args);
      s.resize(s.size() - 1); // remove snprintf's null
    }
    else {
      char saved = *it; // save char overwritten by snprintf's null
      s.insert(it, chars_needed, '\0'); // insert needed space
      it = s.begin() + off; // iterator was invalidated
      err = vsnprintf(&*it, chars_needed + 1, fmt, args);
      *(it + chars_needed) = saved; // restore saved char
    }

    if (err >= 0) { // success
      return;
    }
    err = errno;
    it = s.begin() + off; // above resize might have invalidated 'it'
    // (invalidation is unlikely, but allowed)
    s.erase(it, it + chars_needed);
  }
  string what = stringf("vsnprintf: [%d] ", err);
  what += strerror(err);
  throw runtime_error(what);
}

공용 인터페이스

std::string stringf(char const* fmt, ...) {
  using namespace std;
  string s;
  va_list args;
  va_start(args, fmt);
  int chars_needed = vsnprintf(0, 0, fmt, args);
  va_end(args);
  va_start(args, fmt);
  try {
    vinsertf(s, s.end(), fmt, chars_needed, args);
  }
  catch (...) {
    va_end(args);
    throw;
  }
  va_end(args);
  return s;
}

// these have nearly identical implementations to stringf above:
std::string& appendf(std::string& s, char const* fmt, ...);
std::string& insertf(std::string& s, std::string::iterator it,
                    char const* fmt, ...);

@Neil :에서 man vsnprintf"이 기능은 출력 에러가 발생했을 경우, 인쇄 된 문자의 수 ... 또는 음수 값을 반환 제외 에 대한 snprintf()vsnprintf()n 개의 무제한 인 경우에 인쇄 된 것 문자의 수를 반환하는 ... "따라서 필요한 버퍼 크기를 측정하기 위해 0 버퍼를 사용하는 더미 호출.
Mike DeSimone

@ 체커 : 아, 부스트. 그들이 나에게도 사용하지 못하게하는 엄청난 잠재력. 언젠가는 바라건대. 어쨌든 Boost가 아직 완전히 이해할 수 없을 정도로 커졌습니까? 나는 단지 boost::spirit.
Mike DeSimone

이것은 실제로 Windows 코드입니다. MSDN에서 "_vsnprintf의 경우 쓸 바이트 수가 버퍼를 초과하면 count 바이트가 기록되고 -1이 반환됩니다." 그러나 나는 그것을 Linux로 이식합니다. 내 Linux 앱이 실제로 이것을 사용하는지 또는 큰 버퍼 크기에 대해 테스트했는지 기억할 수 없습니다. 그렇게해야합니다. 감사.

1
어쨌든 Windows 코드를 사용하는 경우 계속 진행 _vscprintf하여 필요한 버퍼 크기를 결정하십시오.
smerlin

6

정렬 된 항목을 예상하는 is_sorted알고리즘을 적용하기 전에 컨테이너를 테스트 하는 유틸리티 include:

  template <
    class FwdIt
  >
  bool is_sorted(FwdIt iBegin, FwdIt iEnd)
  {
    typedef typename std::iterator_traits<FwdIt>::value_type value_type;
    return adjacent_find(iBegin, iEnd, std::greater<value_type>()) == iEnd;
  } // is_sorted

  template <
    class FwdIt,
    class Pred
  >
  bool is_sorted_if(FwdIt iBegin, FwdIt iEnd, Pred iPred)
  {
    if (iBegin == iEnd) return true;
    FwdIt aIt = iBegin;
    for (++aIt; aIt != iEnd; ++iBegin, ++aIt)
    {
      if (!iPred(*iBegin, *aIt)) return false;
    }
    return true;
  } // is_sorted_if

그래, 나는 술어를 부정하고 술어 버전을 사용하는 것이 더 낫다는 것을 안다. adjacent_find:)


1
테스트 만 수행하는 한 assert(): p
wilhelmtell 2010

헝가리 표기법을 사용하면 안됩니다.
the_drow 07:42에

3
@the_drow :이 유용한 댓글에 대해 대단히 감사합니다. :) 나는 그것에 대해 그렇게 팬이 아니지만 내가 일하는 것이 요구 사항입니다 ... 나는 습관을 떨쳐 버리고 내 영혼을 걱정하지 마십시오.)
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);
    }
}

나는 Boost.Bimap라이브러리를 사용하는 것을 선호 합니다 (또는 Boost.MultiIndex더 복잡한 상황에서)
Matthieu M.

1
나는 그것을 얻지 못한다. 왜 assert ()?
Viktor Sehr

@Viktor :에 중복 된 키가 없는지 확인합니다 reverse_map. 고려 map가 (1 -> "일"2 -> "하나") reverse_map하나 개의 요소 얻을 것이다 ( "하나"-> 1). 주장은 그것을 잡을 것입니다. 참조 : bijection
sbk

3
Btw, sbi, assert () 내에 부작용이있는 코드를 가지고 있으면 NDEBUG로 컴파일하고 assert ()가 모두 제거되면 꽤 나빠질 것입니다.
sbk

2
gah, 업데이트 후 내 첫 번째 댓글이 정말 어리석은 것처럼 보입니다. 내 이름을 검색 할 때 stackoverflow가 1 위이므로 미래의 고용주가 이것을 보지 않기를 바랍니다 =)
Viktor Sehr

3

stl_util.h, 많은 고전 (삭제 자 함수 copy_if)과이 항목 (아마도 꽤 일반적이지만 지금까지 응답에 나와 있지 않음)을 살펴보면지도를 검색하고 찾은 값 또는 기본값, getPython의 ala dict:

template<typename K, typename V>
inline V search_map(const std::map<K, V>& mapping,
                    const K& key,
                    const V& null_result = V())
   {
   typename std::map<K, V>::const_iterator i = mapping.find(key);
   if(i == mapping.end())
      return null_result;
   return i->second;
   }

기본 사용하여 null_result기본-건설이의 V많은의 동작과 동일 그대로 std::map의를 operator[]하지만, 기본적으로 건설 V 사용에 대한 옳은 일이 아닌 경우,이지도는 (나를 위해 공통) CONST는 경우에 유용합니다, 또는.


V가 int, float 또는 다른 프리미티브이면 어떻게 될까요?
Inverse

빈 값 초기화는 C ++의 기본 유형에 대해 작동합니다. 정수 및 부동 소수점의 경우 기본 null_result 0이됩니다.
Jack Lloyd

3

다음은 일부 기능에 필요할 수있는 boost.range'ish std-algo 래퍼 위에 구축 된 추가 유틸리티 세트입니다. (작성하기가 쉽지 않습니다. 이것은 흥미로운 것입니다)

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

+1 for_each_permutation (...) , 주로 비슷한 래퍼 =)를 작성했기 때문입니다. 하지만 erase_duplicates (...) 가 서명 된 정수를 반환하는 이유는 무엇입니까?
Viktor Sehr

안녕하세요 빅토르! 이전 직장에서도 for_each_permutation 을 봤어야 했어요. ;) erase_duplicates 는 기록 및 디버깅에 유용한 지워진 요소의 수를 반환합니다.
Macke

흠 그것이 무엇을하는지 깨닫지 못하고 탐색했을 수도 있습니다 .) 어쨌든 정수를 반환하는 이유를 알 수 있습니다. 정수가 서명 된 이유를 얻지 못합니다 (또는 더 구체적으로 말하면 왜 unsigned가 아닙니다 )?
Viktor Sehr

아. 내 편에 게으름 :-P. size_t는 적절한 유형입니다.
Macke

3

데카르트 곱이 필요한 것 같습니다. 예 : {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;
}

InIt2는 입력 반복기 대신 순방향 반복자 여야 합니다. 입력 반복기는 다중 패스에 적합하지 않습니다.

2

이러한 추가 함수를 이름으로 호출하고 다음과 같은 요소 별 작업에 operator + =, operator * = 등을 사용합니다.

    template<typename X> inline void operator+= (std::vector<X>& vec1, const X& value)
    {
      std::transform( vec1.begin(), vec1.end(), vec1.begin(), std::bind2nd(std::plus<X>(),value) );
    }

    template<typename X> inline void operator+= (std::vector<X>& vec1, const std::vector<X>& vec2)
    {
      std::transform( vec1.begin(), vec1.end(), vec2.begin(), vec1.begin(), std::plus<X>() );
    }

이전에 암시 된 다른 간단하고 명백한 래퍼 :

    template<typename X> inline void sort_and_unique(std::vector<X> &vec)
    {
        std::sort( vec.begin(), vec.end() );
        vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() );
    }


    template<typename X> inline void clear_vec(std::vector<X> &vec)
    {
        std::vector<X>().swap(vec);
    }


    template<typename X> inline void trim_vec(std::vector<X> &vec, std::size_t new_size)
    {
        if (new_size<vec.size())
            std::vector<X>(vec.begin(),vec.begin() + new_size).swap(vec);
        else
            std::vector<X>(vec).swap(vec);
    }

7
이러한 연산자는 연산자 오버로딩이 드물게 수행되어야하는 아주 좋은 예입니다. 나는 벡터에 값을 추가vec+=val 할 것이라고 생각했을 것 입니다. ( stackoverflow.com/questions/2551775/ 참조 ) 이제 구현을 보았으므로 이것이의 의미에 대한 올바른 해석이라고 생각합니다 . 나는 우리가하지 않아도 아마뿐만 아니라 그래서 하나, 옳고 그름이 될 것이다 모르겠다 을 위해 . +=+=std::vector
sbi

1
@sbi 동의합니다. 나는 operator+()표준에 대한 놀라운 초기 통찰력을 놓친 것을 발견했습니다 . 나는 보통 플러스 연산자가 보이는 모든 곳에서 O (1) 연산을 기대합니다. C ++는 비싸거나 위험한 일을 더 장황하게 만들거나하기 어렵게 만듭니다. 저는이 방식을 좋아합니다. Java를 살펴보십시오. 최악의 코딩 실수 중 하나는 플러스 연산자의 남용입니다. 물론 다시 말하지만, C ++가 항상 저렴하고 빠른 일을 쉽게 만드는 것은 아니지만 헤이. 좋은 C ++ 프로그래머는 성능을 매우 잘 알고 있습니다. ;)
wilhelmtell 2010

2
op+()모호함으로 인해 전혀 정의해서는 안되는 두 분 모두 동의합니다 . 그러나 벡터는 일반적으로 (수학적) 벡터 공간의 일부이며 두 벡터와 스칼라 곱셈을 더하는 표준 정의가 있습니다. 더 당신의 인수를하려면 다음과 같이 간단한 double뿐만 아니라 벡터, 당신은 두 개의 추가 거라고 그렇다면 double변수를 같은 a+b당신이 새로운 얻을 기대 double아닌 pair이중와 같은의를 (a,b). 스칼라로 곱하는 것도 표준이지만 두 벡터를 곱하는 것은 아닙니다. 그래서 신중하게해야 과부하 ..
덴마크

1

새 항목을 삽입하고 반환합니다. 간단한 이동 의미 체계 push_back(c).swap(value)및 관련 사례에 유용합니다 .

template<class C>
typename C::value_type& push_front(C& container) {
  container.push_front(typename C::value_type());
  return container.front();
}

template<class C>
typename C::value_type& push_back(C& container) {
  container.push_back(typename C::value_type());
  return container.back();
}

template<class C>
typename C::value_type& push_top(C& container) {
  container.push(typename C::value_type());
  return container.top();
}

항목 팝 및 반환 :

template<class C>
typename C::value_type pop_front(C& container) {
  typename C::value_type copy (container.front());
  container.pop_front();
  return copy;
}

template<class C>
typename C::value_type pop_back(C& container) {
  typename C::value_type copy (container.back());
  container.pop_back();
  return copy;
}

template<class C>
typename C::value_type pop_top(C& container) {
  typename C::value_type copy (container.top());
  container.pop();
  return copy;
}

1

IMO에는 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_

1
PairType템플릿을 operator () 로 옮기지 않는 이유는 무엇 입니까? 또한 식별자의 이중 밑줄은 예약되어 있습니다.
GManNickG 2010

@GMan- unary_function내 코드의 어느 시점에서 필요한을 사용할 수 없기 때문 입니다. 이중 밑줄에 대해 알려 주셔서 감사합니다. 변경해야합니다.
rlbond 2010

이는 종속 이름 (argument_type, result_type)을 잘못 사용하므로 컴파일러에서이를 거부해야합니다. "클래스 템플릿 또는 클래스 템플릿의 멤버 정의에서 클래스 템플릿의 기본 클래스가 템플릿 매개 변수에 의존하는 경우 기본 클래스 범위는 정의 시점에서 정규화되지 않은 이름 조회 중에 검사되지 않습니다. 클래스 템플릿이나 멤버 또는 클래스 템플릿이나 멤버의 인스턴스화 중에. " [14.6.2 / 3, C ++ 03]

@Roger Pate : 나는 그 규칙을 몰랐습니다. 이제 수정되었습니다.
rlbond

1
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

포인터 + 바이트 수를 사용하는 많은 함수를 사용해야하는 경우 항상

fun(vec.data(), bytesize(vec));

1

*로 문자열 복제 :

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

0

내가 가장 좋아하는 것 중 하나는 Transposer같은 크기의 컨테이너 튜플의 전치를 찾는 것입니다. 당신이있는 경우 즉, tuple<vector<int>,vector<float>>, 그것은으로 변환합니다 vector<tuple<int, float>>. XML 프로그래밍에 편리합니다. 내가 한 방법은 다음과 같습니다.

#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include <algorithm>
#include <stdexcept>

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/type_traits.hpp>

using namespace boost;

template <class TupleOfVectors>
struct GetTransposeTuple;

template <>
struct GetTransposeTuple<tuples::null_type>
{
  typedef tuples::null_type type;
};

template <class TupleOfVectors>
struct GetTransposeTuple
{
  typedef typename TupleOfVectors::head_type Head;
  typedef typename TupleOfVectors::tail_type Tail;
  typedef typename
    tuples::cons<typename remove_reference<Head>::type::value_type,
                 typename GetTransposeTuple<Tail>::type> type;
};

template <class TupleOfVectors,
          class ValueTypeTuple = 
                typename GetTransposeTuple<TupleOfVectors>::type,
          unsigned int TUPLE_INDEX = 0>
struct Transposer
  : Transposer <typename TupleOfVectors::tail_type,
                ValueTypeTuple,
                TUPLE_INDEX + 1>
{
  typedef typename remove_reference<typename TupleOfVectors::head_type>::type
    HeadContainer;
  typedef typename TupleOfVectors::tail_type Tail;
  typedef Transposer<Tail, ValueTypeTuple, TUPLE_INDEX + 1> super;
  typedef std::vector<ValueTypeTuple> Transpose;

  Transposer(TupleOfVectors const & tuple)
    : super(tuple.get_tail()),
      head_container_(tuple.get_head()),
      head_iter_(head_container_.begin())
  {}

  Transpose get_transpose ()
  {
    Transpose tran;
    tran.reserve(head_container_.size());
    for(typename HeadContainer::const_iterator iter = head_container_.begin();
        iter != head_container_.end();
        ++iter)
    {
      ValueTypeTuple vtuple;
      this->populate_tuple(vtuple);
      tran.push_back(vtuple);
    }
    return tran;
  }

private:

  HeadContainer const & head_container_;
  typename HeadContainer::const_iterator head_iter_;

protected:

  void populate_tuple(ValueTypeTuple & vtuple)
  {
    if(head_iter_ == head_container_.end())
      throw std::runtime_error("Container bound exceeded.");
    else
    {
      vtuple.get<TUPLE_INDEX>() = *head_iter_++;
      super::populate_tuple (vtuple);
    }
  }
};

template <class ValueTypeTuple,
          unsigned int INDEX>
struct Transposer <tuples::null_type, ValueTypeTuple, INDEX>
{
  void populate_tuple(ValueTypeTuple &) {}
  Transposer (tuples::null_type const &) {}
};

template <class TupleOfVectors>
typename Transposer<TupleOfVectors>::Transpose
transpose (TupleOfVectors const & tupleofv)
{
  return Transposer<TupleOfVectors>(tupleofv).get_transpose();
}

int main (void)
{
  typedef std::vector<int> Vint;
  typedef std::list<float> Lfloat;
  typedef std::vector<long> Vlong;

  Vint vint;
  Lfloat lfloat;
  Vlong vlong;

  std::generate_n(std::back_inserter(vint), 10, rand);
  std::generate_n(std::back_inserter(lfloat), 10, rand);
  std::generate_n(std::back_inserter(vlong), 10, rand);

  typedef tuples::tuple<Vint, Lfloat, Vlong> TupleOfV;
  typedef GetTransposeTuple<TupleOfV>::type TransposeTuple;

  Transposer<TupleOfV>::Transpose tran = 
    transpose(make_tuple(vint, lfloat, vlong));
  // Or alternatively to avoid copying
  // transpose(make_tuple(ref(vint), ref(lfloat), ref(vlong)));
  std::copy(tran.begin(), tran.end(),
            std::ostream_iterator<TransposeTuple>(std::cout, "\n"));

  return 0;
}

0

이것이 표준 래퍼로 인정되는지 확실하지 않지만 일반적으로 사용되는 도우미 함수는 다음과 같습니다.

void split(string s, vector<string> parts, string delims);
string join(vector<string>& parts, string delim);
int find(T& array, const V& value);
void assert(bool condition, string message);
V clamp(V value, V minvalue, V maxvalue);
string replace(string s, string from, string to);
const char* stristr(const char* a,const char*b);
string trim(string str);
T::value_type& dyn(T& array,int index);

여기서 T와 V는 템플릿 인수입니다. 마지막 함수는 [] -operator와 동일한 방식으로 작동하지만 필요한 인덱스에 맞게 자동으로 크기 조정됩니다.


1
'assert'라는 이름은 해당 이름의 매크로에 대해 표준 라이브러리에 의해 모든 범위에서 예약됩니다.

1
Windows 또는 mfc 헤더에 선언 된 assert () 매크로도 있다고 생각합니다. 어설 션 대화 상자를 표시하면 경우에 따라 다음 어설 션이 트리거되기 때문에 둘 다 WM_PAINT 이벤트에서 실패합니다. 따라서 결국 버그가있는 구현을 세 번째 구현으로 대체하는 것은 큰 문제가 아닙니다. 당신이해야 할 일은 #include <assert> 다음에 명시 적으로 자신의 assert-macro를 재 선언하거나 그냥 #undef assert를 사용하는 것입니다.
AareP

0

사람들이 이전에 게시 된 것과 마찬가지로, 나는이 반복자 인수를 전달 단순화하기위한 알고리즘의 편의 오버로드. 나는 다음과 같은 알고리즘을 호출합니다.

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

모든 알고리즘을 오버로드 input_sequence_range<>하여 두 개의 입력 반복자 대신 유형의 단일 매개 변수 (단순한 출력이 아닌 모든 입력)를 취했습니다 .

template<typename In>
struct input_sequence_range
: public std::pair<In,In>
{
    input_sequence_range(In first, In last)
        : std::pair<In,In>(first, last)
    { }
};

그리고 이것이 어떻게 iseq()작동하는지 :

template<typename C>
input_sequence_range<typename C::const_iterator> iseq(const C& c)
{
    return input_sequence_range<typename C::const_iterator>(c.begin(),
                                                            c.end());
}

마찬가지로, 저는

  • 상수 _ 반복자
  • 포인터 (기본 배열)
  • 스트림 반복자
  • 모든 범위 [시작, 끝) 균일 한 사용 : 모든 것에 iseq () 사용

3
... 또는 Boost.Range를 사용하고 범위 어댑터와 동료 검토를 거쳐 널리 테스트 된 코드의 이점을 얻을 수 있습니다.
Mankarse 2011-06-03

0

에 대한 순서없는 지우기 std::vector. 에서 요소를 지우는 가장 효율적인 방법 vector이지만 요소의 순서는 유지되지 않습니다. 대부분의 경우 중간에서 항목을 제거하는 데 동일한 패널티가 없기 때문에 다른 컨테이너로 확장하는 요점을 보지 못했습니다. 이미 게시 된 다른 템플릿과 유사하지만 std::swap복사하는 대신 항목을 이동 하는 데 사용 합니다.

template<typename T>
void unordered_erase(std::vector<T>& vec, const typename std::vector<T>::iterator& it)
{
    if (it != vec.end()) // if vec is empty, begin() == end()
    {
        std::swap(vec.back(), *it);
        vec.pop_back();
    }
}

Signum은 유형의 부호를 반환합니다. -1음수, 00 및 1양수 를 반환 합니다 .

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

클램프는 매우 자명하며 주어진 범위 내에 있도록 값을 클램프합니다. 표준 라이브러리에 포함 min되어 max있지만 포함 되지 않는다는 사실은clamp

template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.