Bạn sử dụng chức năng trình bao bọc Thư viện Chuẩn nào của C ++?


81

Câu hỏi này , được hỏi vào sáng nay, khiến tôi tự hỏi bạn nghĩ rằng tính năng nào bị thiếu trong Thư viện chuẩn C ++ và bạn đã làm cách nào để lấp đầy khoảng trống bằng các hàm trình bao bọc. Ví dụ: thư viện tiện ích của riêng tôi có chức năng này cho vector append:

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

và cái này để xóa (nhiều hơn hoặc ít hơn) bất kỳ loại nào - đặc biệt hữu ích cho những thứ như std :: stack:

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

Tôi còn một vài cái nữa, nhưng tôi muốn biết bạn dùng cái nào? Vui lòng giới hạn câu trả lời cho các hàm trình bao bọc - tức là không quá một vài dòng mã.


9
Có tính rằng tôi đã gói phần lớn thuật toán STL để hoạt động trên toàn bộ vùng chứa thay vì phạm vi không, chỉ vì việc gây rối với các trình vòng lặp là một lỗi rất phổ biến :)?
Matthieu M.

2
@Billy thực sự, CW không thực sự là cái cớ để đưa ra những câu hỏi chủ quan. Tôi sẽ thay đổi tiêu đề, điều này sẽ giúp mọi người vui vẻ.

4
@kts: Vì vector :: insert được cung cấp các trình vòng lặp truy cập ngẫu nhiên, một triển khai tốt sẽ sử dụng công văn thời gian biên dịch để tự làm điều đó.

4
Không phải tốt hơn là viết c.swap(C())để xóa một thùng chứa?
Alexandre C.

5
@Alexandre: Điều đó không được phép: nó liên kết tạm thời với một tham chiếu không phải const. C (). Swap (c) sẽ hoạt động.

Câu trả lời:


37

boost :: mảng

chứa (container, val) (khá đơn giản, nhưng tiện lợi).

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 (bắt đầu, kết thúc, giá trị)

Một phiên bản nhanh hơn của std :: remove với ngoại lệ là nó không bảo toàn thứ tự của các đối tượng còn lại.

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

(trong trường hợp vectơ của các loại nhóm (int, float, v.v.) và hầu như tất cả các đối tượng bị loại bỏ, std :: remove có thể nhanh hơn).


4
Có ai khác nghĩ rằng chứa cần một mẫu thứ ba ( bool sorted=false) và một chuyên môn hóa khi nào sorted==truecần gọi binary_searchthay vì findkhông?
KitsuneYMG

5
@kts: Khi bạn biết vùng chứa đã được sắp xếp, chỉ cần gọi trực tiếp binary_search .

2
boost :: array STL tương đương có sẵn trong không gian tên tr1 trên hầu hết các trình biên dịch gần đây (thậm chí CodeWarrior): std :: tr1 :: mảng <>
Klaim

36

Khá thường xuyên, tôi sẽ sử dụng vector như một tập hợp các mục không theo thứ tự cụ thể (và, hiển nhiên, khi tôi không cần kiểm tra nhanh is-this-element-in-the-set). Trong những trường hợp này, việc gọi xóa () là lãng phí thời gian vì nó sẽ sắp xếp lại thứ tự các phần tử và tôi không quan tâm đến thứ tự. Đó là khi hàm O (1) bên dưới trở nên hữu ích - chỉ cần di chuyển phần tử cuối cùng đến vị trí của phần tử bạn muốn xóa:

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

Tốt. Tôi đã không nghĩ đến việc làm như vậy .. :) Cần phải có một trình bao bọc 'Túi' (tương tự như ngăn xếp và hàng đợi) để tăng tốc những hoạt động này khi thứ tự không quan trọng.
Macke

12
Và trong C ++ 0x, v[index] = st::move(v.back()); v.pop_back();hiệu quả như nó có được.
GManNickG

@Matthieu: Nhìn vào dấu thời gian trên đó. : PI nhận thấy vài giờ sau đó, đã quá thời gian chỉnh sửa cho phép.
GManNickG

@GMan: bạn có chắc chắn về điều này không? đối với tôi như v.pop_back () có thể dẫn đến hành vi không xác định vì hàm hủy sẽ được gọi.
Viktor Sehr

1
@Viktor: Move-semantics không có nghĩa là "Tôi lấy tài nguyên của bạn và thế là xong", chúng có nghĩa là "Tôi lấy tài nguyên của bạn và đưa bạn vào trạng thái không có tài nguyên." Nói cách khác, ngữ nghĩa chuyển động của bạn cần đảm bảo đối tượng có thể hủy một cách an toàn; khá dễ thực hiện, chỉ cần đặt con trỏ thành 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&);
};

Được rồi, vì có vẻ như điều này không dễ hiểu như tôi nghĩ, đây là giải thích:
Trong hàm tạo của nó temp_valuelưu trữ một tham chiếu đến một biến và một bản sao giá trị ban đầu của biến. Trong trình hủy của nó, nó khôi phục biến được tham chiếu về giá trị ban đầu của nó. Vì vậy, bất kể bạn đã làm gì với biến giữa xây dựng và phá hủy, nó sẽ được đặt lại khi temp_valueđối tượng vượt ra khỏi phạm vi.
Sử dụng nó như thế này:

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
}

Đây là một cách tiếp cận khác sử dụng thủ thuật bảo vệ phạm vi:

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

Nó chỉ là một mã boiler-plate hơn một chút, nhưng cho phép sử dụng sạch hơn:

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

Tuy nhiên, nó loại bỏ khả năng được sử dụng trong một lớp.


Đây là cách thứ ba để đạt được hiệu quả tương tự (không bị các vấn đề về khả năng ném chất hủy):

Thực hiện:

//none -- it is built into the language

Sử dụng:

#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: Nó để khôi phục giá trị sau này, tự động. (Và val nên được loại khỏi ctor.)

Xin lỗi, tôi vẫn bị lạc (tôi mới làm quen với C ++), có ai có thể chết lặng ngay không?
dreamlax

1
@dreamlax: Tôi đã thêm một số văn bản mô tả mô tả nó vào câu trả lời. Bây giờ nó có thể hiểu được hay tôi nên tìm hiểu sâu hơn về chi tiết?
sbi

1
oh, cái thứ hai là khá thông minh.
jalf

1
Hmm trường hợp sử dụng thực tế cho điều này là gì?
paulm,

22

Không hẳn là một cái bọc, mà là cái mất tích khét tiếng copy_if. Từ đây

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
Không trả lời câu hỏi, không phải là trình bao bọc cho stdlib.

10
@Roger Pate, vâng tôi biết, đó là lý do tại sao câu trả lời bắt đầu bằng những từ "Không thực sự là một trình bao bọc, nhưng ....".
Glen

1
Chi tiết triển khai @Roger. Nếu bạn thực sự muốn, bạn có thể thực hiện nó về mặt 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;}

2
Tôi cũng có những thứ này. :) +1 Đối với những gì nó đáng giá, bạn chỉ cần hai (bỏ qua các phiên bản const). Khi mảng là const, Tsẽ là const Uvà bạn nhận được chức năng mong muốn.
GManNickG

@GMan: Có một số phiên bản GCC sẽ không biên dịch một số mã chỉ với các constphiên bản không phải phiên bản, đó là lý do tại sao các constphiên bản ở đó. Vì đó có thể là lỗi của phiên bản GCC cụ thể đó, nên tôi sẽ xóa chúng.
sbi

1
@Marcus: Chúng cũ hơn nhiều so với Boost.Range. :)
sbi

4
@Roger: Nó bao bọc các mảng để chúng được sử dụng với lib tiêu chuẩn. Của bạn đây. :)
sbi

2
@Stacked, @sbe: Mảng không bao giờ được chuyển theo giá trị, bất kể có hay không có &. Ở &đó để cho phép loại trừ độ dài của mảng.
Mankarse

12

Đôi khi tôi cảm thấy như tôi đang ở begin()end()địa ngục. Tôi muốn có một số chức năng như:

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

và những người tương tự khác cho std::find, std::for_eachvà về cơ bản tất cả các thuật toán STL.

Tôi cảm thấy rằng sort(x)đọc / hiểu nhanh hơn nhiều sort(x.begin(), x.end()).


1
dấu; sử dụng ´sort (boost :: begin (x), boost: end (x)); ´ để thay thế, và bạn cũng có thể sắp xếp các mảng.
Viktor Sehr

4
Boost.Range v2 có các bộ điều hợp như thế này cho toàn bộ thư viện tiêu chuẩn.
ildjarn

9

Tôi không còn sử dụng cái này nhiều nữa, nhưng nó từng là một mặt hàng chủ lực:

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

Sẽ cập nhật với nhiều hơn nữa khi tôi nhớ chúng. : P


3
Hehe - một lối tắt cho boost::lexical_cast<t, t>.
Billy ONeal

1
yeah, vĩ đại của một rằng nếu bạn không muốn tăng kéo vào một dự án
Steve

11
@BillyONeal: Đó là lý do tôi không sử dụng nó nữa. @Steve: Đó là lý do tôi vẫn sử dụng nó.
Jon Purdy,

Sẽ rất tốn kém không cần thiết nếu gọi nó bằng a char*hoặc a std::string. Có thể một chuyên môn hóa mẫu đang theo thứ tự?
wilhelmtell

Nếu tôi nhớ không boost::lexical_castlầm , có một loạt các chuyên ngành và kiểm tra lỗi như vậy. Tuy nhiên, để xâu chuỗi số lẻ, điều này hoạt động tốt.
Jon Purdy

9

Tất nhiên, chức năng tiện ích trong hộp công cụ của mọi người copy_if. Không thực sự là một trình bao bọc.

Một công cụ trợ giúp khác mà tôi thường sử dụng là deleter, một công cụ tôi sử dụng std::for_eachđể xóa tất cả các con trỏ trong một vùng chứa.

[sửa] Tìm hiểu "sth.h" của tôi, tôi cũng tìm thấy vector<wstring> StringSplit(wstring const&, wchar_t);


+1 cho các chức năng xóa. Bộ điều khiển trình duyệt của tôi hoạt động tốt với hầu hết các vùng chứa, tuy nhiên, tôi đã cố gắng làm cho nó hoạt động với std :: map trong đó khóa hoặc giá trị là con trỏ. Tôi đã thử sử dụng các đặc điểm kiểu để giải quyết vấn đề, nhưng không thực sự đi được xa do hạn chế về thời gian. Bạn đã giải quyết vấn đề này hay bạn coi nó là một vấn đề?
Glen

2
@Matthieu M. Thật không may, không phải tất cả chúng ta đều có thể sử dụng boost.
Glen

@Glen: Tất cả những rắc rối này có thể được giải quyết bằng cách sử dụng con trỏ thông minh, tốt nhất là (nhưng không nhất thiết) từ boost. Như một tác dụng phụ quan trọng, các vùng chứa có con trỏ đến các đối tượng được phân bổ động cũng đột nhiên trở thành ngoại lệ an toàn.
sbi

@Glen tại đây, tại đây dành cho các dự án quy định không có thư viện nào khác ngoài STD, tối đa và bao gồm Boost hoặc TR1.
wheaties

7
@ những nạn nhân không may của sự ngu ngốc của công ty: lấy một ngoại lệ cho các thư viện nội bộ, sau đó nhập các phần hữu ích của Boost vào thư viện nội bộ. Ngay cả trong chính trị, mọi vấn đề đều có thể được giải quyết bằng một mức độ khác của hướng đi.
MSalters

9

Tôi có một tiêu đề đặt phần sau trong không gian tên "dùng":

// 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
Điều đó split()nuốt chửng bất kỳ lỗi nào xảy ra std::getline(), âm thầm trả về một vectơ quá ngắn.
sbi

tất nhiên, bạn nên kiểm tra size()kết quả trước khi lấy các chuỗi của bạn.
Evan Teran

Và làm thế nào tôi biết kết quả phải có bao nhiêu chuỗi?
sbi

2
@sbi: Nhận xét của bạn đã khiến tôi quan tâm đến đỉnh điểm về những gì thực sự có thể xảy ra sai lầm (ngoài việc chuỗi đơn giản là không có đủ mã thông báo để nhận) với vòng lặp stringstream/ getline. Tôi đã đặt một câu hỏi về nó ở đây: stackoverflow.com/questions/2562906/…
Evan Teran,

2
@Evan: Tôi đứng sửa lại. Xem nhận xét của tôi tại stackoverflow.com/2563542#2563542 . Lấy làm tiếc.
sbi

8

eraseThuật toán mất tích khét tiếng :

  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, sắp đăng tương đương chính xác của tôi. Altough Tôi đã đặt tên nó là remove_erase (...)
Viktor Sehr.

2
Vấn đề duy nhất với điều này là nó phá vỡ thành ngữ xóa-xoá ngữ nghĩa được mong đợi trong STL. Bạn cần thành ngữ xóa - loại bỏ với bất kỳ thuật toán nào đã loại bỏ ngữ nghĩa - không chỉ std::remove. Ví dụ std::unique,.
Billy ONeal

Chà, tôi có các chuyển thể vùng chứa của hầu hết các thuật toán STL cho việc này :) Nhưng đây là thuật toán tôi sử dụng nhiều nhất, thường nếu tôi muốn sự độc đáo, tôi sử dụng a setđể bắt đầu.
Matthieu M.

@Matthieu M.: Chỉ cần ghi nhớ bằng cách làm điều này, bạn sẽ có những người làm việc với STL mọi lúc đều có chuông báo thức vang lên trong đầu "CẢNH BÁO: XÓA THUẬT TOÁN GIỐNG MÀ KHÔNG CÓ GỌI LỖI !!" . Không có gì sai với nó thực sự, nhưng đó không phải là điều tôi sẽ làm nếu tôi cần chia sẻ mã của mình với nhiều lập trình viên. Chỉ 2 xu của tôi.
Billy ONeal

1
@Billy: đó là lý do tại sao tôi gọi nó là xóa chứ không phải xóa. Tôi không thể làm gì nhiều ngoài việc để họ tham khảo mã. Rất may với IDE hiện đại, định nghĩa chỉ là một cú nhấp chuột :)
Matthieu M.

7

Kết thúc 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

Mục tiêu là tách định dạng khỏi đầu ra mà không gặp rắc rối với sprintf và ilk của nó. Nó không đẹp, nhưng nó rất hữu ích, đặc biệt là nếu các nguyên tắc viết mã của bạn cấm iostream.


Đây là phiên bản phân bổ khi cần thiết, từ Neil Butterworth. [Xem lịch sử sửa đổi cho phiên bản của Mike, mà tôi đã xóa dưới dạng một tập hợp con của hai phiên bản còn lại. Nó tương tự như của Neil, ngoại trừ cái sau là ngoại lệ an toàn bằng cách sử dụng vectơ thay vì xóa []: ctor của chuỗi sẽ gây ra lỗi phân bổ. Mike's cũng sử dụng kỹ thuật tương tự được hiển thị sau để xác định kích thước trước. –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] );
}

Đây là phiên bản xác định kích thước cần thiết trước, từ Roger Pate . Điều này yêu cầu chuỗi std :: có thể ghi, được cung cấp bởi các triển khai phổ biến, nhưng được yêu cầu rõ ràng bởi C ++ 0x. [Xem lịch sử sửa đổi cho phiên bản của Marcus, mà tôi đã xóa vì nó hơi khác nhưng về cơ bản là một tập hợp con của phiên bản bên dưới. –RP]

Thực hiện

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

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

Giao diện công khai

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: Từ man vsnprintf: "Những chức năng trả lại số ký tự in ... hoặc một giá trị âm nếu một lỗi sản xuất xảy ra, ngoại trừ cho snprintf()vsnprintf(), mà trả lại số ký tự mà có thể đã được in nếu n là không giới hạn ... "Do đó, lệnh gọi giả với bộ đệm 0 để đo kích thước bộ đệm cần thiết.
Mike DeSimone

@Checkers: Ah, Tăng. Tiềm năng lớn đến mức họ cũng không cho tôi sử dụng. Một ngày nào đó, hy vọng. Dù sao, Boost đã đủ lớn để không thể hiểu hết được chưa? Tôi rất vui khi có được boost::spirit.
Mike DeSimone

Đây thực sự là mã Windows - từ MSDN "Đối với _vsnprintf, nếu số byte để ghi vượt quá bộ đệm, thì số byte được ghi và –1 được trả về." nhưng tôi chuyển nó sang Linux. Tôi không thể nhớ liệu ứng dụng Linux của mình có thực sự sử dụng điều này hay không, hay liệu tôi đã thử nghiệm nó cho kích thước bộ đệm lớn ở đó hay chưa - phải làm như vậy. Cảm ơn.

1
nếu bạn vẫn sử dụng mã Windows, hãy tiếp tục và sử dụng _vscprintfđể xác định kích thước cần thiết của bộ đệm.
smerlin

6

Các is_sortedtiện ích, để đựng thử nghiệm trước khi áp dụng các thuật toán như includemà mong đợi một mục được sắp xếp:

  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

Vâng, tôi biết, sẽ tốt hơn nếu phủ định vị ngữ và sử dụng phiên bản vị ngữ của adjacent_find:)


1
chừng nào bạn chỉ làm bài kiểm tra trong một assert(): p
wilhelmtell

Bạn không nên sử dụng ký hiệu Hungary.
the_drow,

2
@the_drow: cảm ơn bạn rất nhiều vì nhận xét hữu ích này :) Tôi không quá hâm mộ nó, nhưng đó là yêu cầu của nơi tôi làm việc ... Tôi đã bỏ thói quen từ đó, đừng lo lắng cho tâm hồn của tôi;)
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);
    }
}

Tôi thích sử dụng các Boost.Bimapthư viện (hoặc Boost.MultiIndexcho các tình huống phức tạp hơn)
Matthieu M.

1
Tôi không hiểu, tại sao lại khẳng định ()?
Viktor Sehr

@Viktor: để đảm bảo không có khóa trùng lặp trong reverse_map . Xét maphas (1 -> "one"; 2 -> "one") reverse_mapsẽ nhận được một phần tử ("one" -> 1). Quả quyết sẽ nắm bắt được điều đó. Xem thêm: bijection
sbk

3
Btw, sbi, có mã với các tác dụng phụ trong khẳng định () sẽ khiến bạn khá tệ khi bạn biên dịch với NDEBUG và khẳng định () bị loại bỏ hoàn toàn.
sbk

2
gah, sau khi cập nhật nhận xét đầu tiên của tôi trông thực sự ngu ngốc, stackoverflow là # 1 khi googling tên của tôi nên tôi hy vọng không có chủ nhân tương lai nào nhìn thấy điều này =)
Viktor Sehr

3

Nhìn vào của tôi stl_util.h, nhiều hàm kinh điển (hàm deleter, copy_if) và cả hàm này (có lẽ cũng khá phổ biến, nhưng tôi không thấy nó được đưa ra trong các câu trả lời cho đến nay) để tìm kiếm qua bản đồ và trả về giá trị được tìm thấy hoặc mặc định, ala gettrong Python dict:

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

Sử dụng mặc định null_result của một cấu trúc mặc định Vcũng giống như hành vi của std::map's operator[], nhưng điều này hữu ích khi bản đồ là const (đối với tôi phổ biến) hoặc nếu V được xây dựng mặc định không phải là thứ thích hợp để sử dụng.


Điều gì sẽ xảy ra nếu V là một int hoặc float hoặc một số nguyên thủy khác?
Đảo ngược

Khởi tạo giá trị trống hoạt động cho các kiểu cơ bản trong C ++. Đối với số nguyên và nổi, điều đó sẽ làm cho null_result mặc định 0.
Jack Lloyd

3

Đây là tập hợp các extra-utils của tôi, được xây dựng dựa trên trình bao bọc boost.range'ish std-algo mà bạn có thể cần cho một số chức năng. (viết thì tầm thường, đây là điều thú vị)

#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 cho for_each_permutation (...) , chủ yếu là vì tôi đã viết một trình bao bọc tương tự =). Nhưng tại sao delete_duplicates (...) trả về một int đã ký?
Viktor Sehr,

Chào Viktor! Chắc hẳn bạn cũng đã từng thấy for_each_permutation ở công việc trước đây của mình, tôi nghĩ vậy. ;) delete_duplicates trả về số phần tử bị xóa, rất hữu ích cho việc ghi nhật ký & gỡ lỗi.
Macke

Hmm có thể đã duyệt qua nó mà không nhận ra nó làm gì ;) , dù sao, tôi hiểu tại sao nó trả về một số nguyên, tôi chỉ không hiểu tại sao số nguyên được ký (hoặc cụ thể hơn; tại sao nó không có dấu )?
Viktor Sehr

Ah. Chỉ là sự lười biếng về phía tôi :-P. size_t là kiểu thích hợp.
Macke

3

Tôi dường như cần một tích số Descartes, ví dụ: {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;
}

Lưu ý rằng InIt2 phải là một trình vòng lặp chuyển tiếp thay vì một trình vòng lặp đầu vào. Các trình vòng lặp đầu vào không thích hợp cho nhiều lần chuyển.

2

Tôi sẽ gọi một hàm append như vậy bằng tên của nó và sẽ sử dụng operator + =, operator * =, v.v. cho các phép toán thông minh phần tử, chẳng hạn như:

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

một số trình bao bọc đơn giản và rõ ràng khác như được ngụ ý trước đây:

    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
Các toán tử này là một ví dụ rất tốt tại sao việc nạp chồng toán tử hiếm khi được thực hiện. Tôi đã nghĩ rằng, vec+=valsẽ thêm một giá trị vào vector. (Xem stackoverflow.com/questions/2551775/. ) Bây giờ tôi đã thấy cách triển khai của bạn, tôi nghĩ đây cũng là cách giải thích đúng về ý nghĩa của +=. Tôi sẽ không biết cái nào sẽ là đúng hay sai, vì vậy nó có thể chỉ là tốt mà chúng ta không có +=cho std::vector.
sbi

1
@sbi Tôi đồng ý. Tôi thấy operator+()thiếu một cái nhìn sâu sắc đáng kinh ngạc về tiêu chuẩn. Tôi thường mong đợi một phép toán O (1) ở mọi nơi tôi thấy toán tử cộng. C ++ làm cho những thứ tốn kém hoặc nguy hiểm trở nên dài dòng hơn hoặc khó thực hiện hơn, và tôi thích nó theo cách này. Hãy nhìn vào Java: một trong những sai lầm mã hóa tồi tệ nhất đó là việc lạm dụng toán tử cộng. Tất nhiên, một lần nữa, C ++ không phải lúc nào cũng khiến mọi thứ rẻ và nhanh trở nên dễ dàng, nhưng này. Các lập trình viên C ++ giỏi có nhận thức rất nhiều về hiệu suất. ;)
wilhelmtell

2
Tôi đồng ý với cả hai bạn rằng op+()không nên định nghĩa gì cả do sự mơ hồ của nó. Nhưng vectơ thường là một phần của không gian vectơ (toán học) và có một định nghĩa chính tắc về phép cộng hai vectơ và phép nhân vô hướng. Để tiếp tục lập luận của bạn: một đơn giản doublecũng là một vectơ, vì vậy nếu bạn thêm hai doublebiến giống như vậy a+bthì bạn sẽ mong đợi nhận được một biến mới doublechứ không phải một pairbiến giống như kép (a,b). Nhân với một vô hướng cũng là chính tắc, nhưng nhân hai vectơ thì không. Vì vậy, quá tải cần được thực hiện cẩn thận ..
Dane

1

Chèn một mục mới và trả lại, hữu ích cho các ngữ nghĩa di chuyển đơn giản như push_back(c).swap(value)và các trường hợp liên quan.

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

Mở và trả lại một mục:

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 cần có nhiều chức năng hơn cho 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
Tại sao không chuyển PairTypemẫu sang toán tử ()? Ngoài ra, dấu gạch dưới kép trong mã định danh được dành riêng.
GManNickG

@GMan - Bởi vì sau đó bạn không thể sử dụng unary_function, điều mà tôi cần tại một số điểm trong mã của mình. Đối với dấu gạch dưới kép, cảm ơn đã cho tôi biết - Tôi sẽ cần thay đổi điều đó.
rlbond

Điều này sử dụng các tên phụ thuộc (kiểu_ đối số, kiểu_kết quả) không chính xác và các trình biên dịch được yêu cầu từ chối nó. "Trong định nghĩa của mẫu lớp hoặc thành viên của mẫu lớp, nếu lớp cơ sở của mẫu lớp phụ thuộc vào tham số khuôn mẫu, phạm vi lớp cơ sở không được kiểm tra trong quá trình tra cứu tên không đủ tiêu chuẩn hoặc tại điểm định nghĩa của mẫu lớp hoặc thành viên hoặc trong quá trình khởi tạo mẫu lớp hoặc thành viên. " [14.6.2 / 3, C ++ 03]

@Roger Pate: Tôi không biết về quy tắc đó. Nó đã được sửa ngay bây giờ.
rlbond

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

Nếu bạn cần sử dụng nhiều hàm lấy con trỏ + số byte, nó luôn

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

1

Nhân đôi một chuỗi với *:

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

Một trong những trò yêu thích của tôi là Transposertìm thấy sự chuyển vị của một loạt các thùng chứa có cùng kích thước. Tức là, nếu bạn có a tuple<vector<int>,vector<float>>, nó sẽ chuyển nó thành a vector<tuple<int, float>>. Có ích trong lập trình XML. Đây là cách tôi đã làm điều đó.

#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

Không chắc liệu những thứ này có đủ điều kiện là trình bao bọc std hay không, nhưng các hàm trợ giúp thường được sử dụng của tôi là:

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à V ở đây là các đối số mẫu. Hàm cuối cùng hoạt động theo cách tương tự như [] -operator, nhưng với việc tự động thay đổi kích thước để phù hợp với chỉ mục cần thiết.


1
Tên 'khẳng định' được dành riêng (trong tất cả các phạm vi) bởi thư viện chuẩn cho macro của tên đó.

1
Tôi nghĩ rằng cũng có một macro khẳng định () được khai báo trong các tiêu đề cửa sổ hoặc mfc. Cả hai đều không thành công trong sự kiện WM_PAINT, vì hiển thị hộp thoại xác nhận sẽ kích hoạt xác định tiếp theo trong một số trường hợp. Vì vậy, cuối cùng, không phải là vấn đề lớn nếu thay thế các triển khai lỗi đó bằng một triển khai thứ ba. Tất cả những gì bạn phải làm là khai báo lại macro khẳng định của riêng mình sau #include <assert> hoặc chỉ sử dụng #undef khẳng định.
AareP

0

Tương tự như những gì mọi người đã đăng trước đây, tôi có các thuật toán quá tải tiện lợi để đơn giản hóa việc truyền các đối số của trình lặp. Tôi gọi các thuật toán như thế này:

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

Tôi đã quá tải tất cả các thuật toán để chúng nhận một tham số duy nhất của kiểu input_sequence_range<>thay vì hai trình vòng lặp đầu vào (đầu vào như trong bất kỳ thứ gì không chỉ là đầu ra).

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

Và đây là cách iseq()hoạt động:

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

Tương tự, tôi có chuyên môn cho

  • const_iterator
  • con trỏ (mảng nguyên thủy)
  • trình vòng lặp dòng
  • bất kỳ phạm vi nào [bắt đầu, kết thúc) chỉ để sử dụng thống nhất: sử dụng iseq () cho mọi thứ

3
... hoặc bạn có thể chỉ sử dụng Boost.Range và nhận được những lợi ích của bộ điều hợp phạm vi và mã được kiểm tra rộng rãi, được đánh giá ngang hàng.
Mankarse

0

Xóa không theo thứ tự cho std::vector. Cách hiệu quả nhất để xóa một phần tử khỏi a vectornhưng nó không bảo toàn thứ tự của các phần tử. Tôi không thấy điểm mở rộng nó cho các thùng chứa khác vì hầu hết không có hình phạt giống nhau cho việc loại bỏ các mục ở giữa. Nó tương tự như một số mẫu khác đã được đăng nhưng nó sử dụng std::swapđể di chuyển các mục thay vì sao chép.

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 trả về dấu của một loại. Trả về số -1âm, 0số không và số 1dương.

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

Kẹp khá tự giải thích, nó kẹp một giá trị để nó nằm trong phạm vi nhất định. Tôi làm tôi bối rối rằng Thư viện Chuẩn bao gồm minmaxnhưng khôngclamp

template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.