【问题标题】:Using boost::bind on std::string::find fails to compile在 std::string::find 上使用 boost::bind 无法编译
【发布时间】:2012-03-15 16:50:21
【问题描述】:

我有以下代码:

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 相关的编译器错误对我来说几乎是不可读和无益的,所以我希望有人能帮助我弄清楚发生了什么。提前致谢。

【问题讨论】:

  • 获取标准库(成员)函数的地址是非法的;我不知道您为什么忽略了对上一个(已删除)问题的评论。
  • @ildjarn 2 个原因:1)我发的太早了;我的代码很垃圾,2)您没有解释原因(没有引用标准等)。此外,您的评论没有解决编译器失败的原因,即使它是非法的,我仍然想知道原因。
  • 有 4 个不同的std::string::find。编译器应该选择哪一个?
  • @Robert :关于 2),我并没有试图回答你的问题(因此发表评论而不是答案),我只是指出你想要实现的是无论如何都失败了。
  • @ildjarn 我说过“......而且你的 comment 没有解决......”。而且,无论您选择哪种方式来解决我的问题,您都还没有在推理背后展示任何类型的事实。

标签: c++ boost stl compiler-errors boost-bind


【解决方案1】:

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 lambda 看起来最可读和优雅:

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】:

    bind 的参数类型之间没有任何关系(正交模板类型),只有在 body 中,编译器才能确定需要 find 的哪个重载。事实上,编译器只允许查看函数声明来确定要传递的内容,并且 find 可能存在模棱两可的重载,并且编译器无法使用绑定参数类型来帮助确定使用哪一个。

    在这种情况下,我认为编写一个 5 行仿函数来为您进行嵌套字符串搜索可能会更简单。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-29
      • 2014-05-23
      • 1970-01-01
      • 2015-02-12
      • 1970-01-01
      • 2017-06-04
      相关资源
      最近更新 更多