Использование boost :: bind для std :: string :: find не компилируется - PullRequest
3 голосов
/ 15 марта 2012

У меня есть следующий код:

int MimeDocument::GetAttachmentId( std::string const& content_id )
{
    using namespace boost::lambda;
    using boost::lambda::_1;
    using boost::bind;

    int id = 0;

    std::vector<std::string>::iterator it =
        std::find_if( attachment_list_.begin(), attachment_list_.end(),
            bind( &std::string::find, content_id, _1 ) != std::string::npos
        );

    if( it != attachment_list_.end() ) {
        id = std::distance( attachment_list_.begin(), it );
    }

    return id;
}

Что при компиляции на MSVC9 SP1 приводит к множеству ошибок C2780 компилятора. Вот только некоторые из верхней части списка:

1>c:\code\work\cmake-mds\server\gmmserver\domino\server\interface\dimime.cpp(210) : error C2780: 'boost::_bi::bind_t<_bi::dm_result<MT::* ,A1>::type,boost::_mfi::dm<M,T>,_bi::list_av_1<A1>::type> boost::bind(M T::* ,A1)' : expects 2 arguments - 3 provided
1>        c:\code\work\cmake-mds\build-vc9\third_party\boost\1.48.0\include\boost\bind\bind.hpp(1728) : see declaration of 'boost::bind'
1>c:\code\work\cmake-mds\server\gmmserver\domino\server\interface\dimime.cpp(210) : error C2780: 'boost::_bi::bind_t<Rt2,boost::_mfi::cmf8<R,T,B1,B2,B3,B4,B5,B6,B7,B8>,_bi::list_av_9<A1,A2,A3,A4,A5,A6,A7,A8,A9>::type> boost::bind(boost::type<T>,R (__thiscall T::* )(B1,B2,B3,B4,B5,B6,B7,B8) const,A1,A2,A3,A4,A5,A6,A7,A8,A9)' : expects 11 arguments - 3 provided
1>        c:\code\work\cmake-mds\build-vc9\third_party\boost\1.48.0\include\boost\bind\bind_mf2_cc.hpp(223) : see declaration of 'boost::bind'
1>c:\code\work\cmake-mds\server\gmmserver\domino\server\interface\dimime.cpp(210) : error C2780: 'boost::_bi::bind_t<Rt2,boost::_mfi::mf8<R,T,B1,B2,B3,B4,B5,B6,B7,B8>,_bi::list_av_9<A1,A2,A3,A4,A5,A6,A7,A8,A9>::type> boost::bind(boost::type<T>,R (__thiscall T::* )(B1,B2,B3,B4,B5,B6,B7,B8),A1,A2,A3,A4,A5,A6,A7,A8,A9)' : expects 11 arguments - 3 provided
1>        c:\code\work\cmake-mds\build-vc9\third_party\boost\1.48.0\include\boost\bind\bind_mf2_cc.hpp(212) : see declaration of 'boost::bind'

Любые ошибки компилятора, связанные с boost, практически не читаются и бесполезны для меня, поэтому я надеюсь, что кто-нибудь поможет мне разобраться в происходящем. Заранее спасибо.

Ответы [ 2 ]

5 голосов
/ 15 марта 2012

Существует четыре перегрузки std::string::find:

size_t find(const string& str, size_t pos = 0) const;
size_t find(const char* s, size_t pos, size_t n) const;
size_t find(const char* s, size_t pos = 0) const;
size_t find(char c, size_t pos = 0) const;

Следовательно, нужно помочь компилятору выбрать один (устранить неоднозначность), указав, к какой конкретной перегруженной функции относится адрес, например ::

boost::bind( static_cast<size_t(std::string::*)(const std::string&, size_t) const>(&std::string::find), content_id, _1, 0)

Довольно уродливо, не правда ли?

Обратите внимание, что std::string::find() возвращает std::string::npos (чаще всего size_t(-1)) при неудачном поиске. Затем он преобразует size_t(-1) в bool(true) и в результате std::find_if() будет возвращать свой первый аргумент независимо от того, каковы остальные аргументы.

Результат std::string::find() необходимо сравнить с std::string::npos. Используя boost::bind, это будет выглядеть так:

// ...

std::vector<std::string>::iterator it = std::find_if(
      attachment_list_.begin()
    , attachment_list_.end()
    , boost::bind(
          std::not_equal_to<std::string::size_type>()
        , std::string::npos
        , boost::bind(
              static_cast<size_t(std::string::*)(const std::string&, size_t) const>(&std::string::find)
            , &content_id // pass by pointer, don't copy
            , _1
            , 0)
            )
    );

Который тоже не выглядит слишком читабельным.

Может быть чуть-чуть более читабельно с boost::lambda::bind:

#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>

// ...

std::vector<std::string>::iterator it =
    std::find_if(
          attachment_list_.begin()
        , attachment_list_.end()
        , boost::lambda::constant(std::string::npos) != boost::lambda::bind(
              static_cast<size_t(std::string::*)(const std::string&, size_t) const>(&std::string::find)
            , &content_id // pass by pointer, don't copy
            , boost::lambda::_1
            , 0
            )
    );

Он выглядит наиболее читаемым и элегантным с C ++ 11 лямбда :

std::vector<std::string>::iterator it = std::find_if(
      attachment_list_.begin()
    , attachment_list_.end()
    , [&content_id](std::string const& i) { return std::string::npos != content_id.find(i); }
    );

Далее я заметил, что id, возвращаемое при неудачном поиске, равно 0. Это то же самое значение, которое возвращается, когда поиск завершается по первому элементу. Другими словами, вызывающая сторона этой функции не сможет различить неудачный поиск и соответствие первого (0-го) элемента.

Наиболее простой и переносимый способ использовать простой цикл для поиска здесь:

std::string* MimeDocument::GetAttachmentId(std::string const& content_id) {
    for(  std::vector<std::string>::iterator i(attachment_list_.begin()), j(attachment_list_.end())
        ; i != j
        ; ++i
        ) {
        if(std::string::npos != content_id.find(*i))
            return &*i;
    }
    return NULL;
}

Используя эту версию, вызывающий абонент может легко определить между успешным и неудачным поиском и при необходимости найти соответствующий индекс:

MimeDocument doc;
// ... populate doc
if(std::string* found = doc.GetAttachmentId("1")) {
    // the search was successful.
    size_t found_index = found - &doc.attachment_list_.front();
}

Итак, подбери свой яд ...

2 голосов
/ 15 марта 2012

Типы параметров для bind никак не связаны друг с другом (типы ортогональных шаблонов), и только внутри body компилятор может определить, какая перегрузка find необходима. Фактически, компилятору разрешается только просматривать объявление функции, чтобы выяснить, что передать, и существуют неоднозначные возможные перегрузки find, и компилятор не может использовать связанный тип аргумента, чтобы помочь выяснить, какой из них использовать.

В этом случае, я думаю, может быть проще написать 5-строчный функтор для поиска по вложенной строке.

...