将boost::unordered_set作为结果映射传递给boost::split函数

5

请问是否可以将boost :: unordered_set作为boost :: split的第一个参数传递?在libboost1.42-dev下,这似乎会引起问题。以下是一个导致问题的小型示例程序,请将其命名为test-split.cc:

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/unordered_set.hpp>
#include <string>

int main(int argc, char **argv) {
  boost::unordered_set<std::string> tags_set;
  boost::split(tags_set, "a^b^c^",
               boost::is_any_of(std::string(1, '^')));
  return 0;
}

如果我运行以下命令:

g++ -o test-split test-split.cc; valgrind ./test-split

我在valgrind中收到了许多投诉,比如下面的一条(有时我也会看到没有valgrind的coredumps,尽管这似乎取决于时间):

==16843== Invalid read of size 8
==16843==    at 0x4ED07D3: std::string::end() const (in /usr/lib/libstdc++.so.6.0.13)
==16843==    by 0x401EE2: unsigned long boost::hash_value<char, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /tmp/test-split)
...
==16843==    by 0x402248: boost::unordered_set<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> >& boost::algorithm::split<boost::unordered_set<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> >, char const [26], boost::algorithm::detail::is_any_ofF<char> >(boost::unordered_set<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> >&, char const (&) [26], boost::algorithm::detail::is_any_ofF<char>, boost::algorithm::token_compress_mode_type) (in /tmp/test-split)
==16843==    by 0x40192A: main (in /tmp/test-split)
==16843==  Address 0x5936610 is 0 bytes inside a block of size 32 free'd
==16843==    at 0x4C23E0F: operator delete(void*) (vg_replace_malloc.c:387)
==16843==    by 0x4ED1EE8: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/libstdc++.so.6.0.13)
==16843==    by 0x404A8B: void boost::unordered_detail::hash_unique_table<boost::unordered_detail::set<boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> > >::insert_range_impl<boost::transform_iterator<boost::algorithm::detail::copy_iterator_rangeF<std::string, char const*>, boost::algorithm::split_iterator<char const*>, boost::use_default, boost::use_default> >(std::string const&, boost::transform_iterator<boost::algorithm::detail::copy_iterator_rangeF<std::string, char const*>, boost::algorithm::split_iterator<char const*>, boost::use_default, boost::use_default>, boost::transform_iterator<boost::algorithm::detail::copy_iterator_rangeF<std::string, char const*>, boost::algorithm::split_iterator<char const*>, boost::use_default, boost::use_default>) (in /tmp/test-split)
...
==16843==    by 0x402248: boost::unordered_set<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> >& boost::algorithm::split<boost::unordered_set<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> >, char const [26], boost::algorithm::detail::is_any_ofF<char> >(boost::unordered_set<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::string> >&, char const (&) [26], boost::algorithm::detail::is_any_ofF<char>, boost::algorithm::token_compress_mode_type) (in /tmp/test-split)
==16843==    by 0x40192A: main (in /tmp/test-split)

这是一个Debian Squeeze系统,以下是相关的系统信息:
$ g++ --version
g++ (Debian 4.4.5-2) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ dpkg -l | grep boost
ii  libboost-iostreams1.42.0            1.42.0-4                     Boost.Iostreams Library
ii  libboost1.42-dev                    1.42.0-4                     Boost C++ Libraries development files
$ uname -a
Linux gcc44-buildvm 2.6.32-5-amd64 #1 SMP Fri Sep 17 21:50:19 UTC 2010 x86_64 GNU/Linux

然而,如果我将libboost1.42-dev降级为libboost1.40-dev,则代码似乎可以正常工作。那么这是boost 1.42中的一个bug,还是我在使用boost::split时传入了无法处理序列的容器?谢谢!


就此而言,我只能够使用boost::unordered_set重现这些valgrind错误,而GCC的std::unordered_set则没有任何问题。 - Cubbi
11
也许下面的例子值得考虑,因为它们更简单、更高效:http://www.codeproject.com/KB/recipes/Tokenizer.aspx。特别是其中的“一些简单示例”部分。 - Matthieu N.
3个回答

2
这已经在boost-users邮件列表中确认是boost :: unordered_set实现中的一个错误。邮件列表上有一个补丁可用,并且很快就会进行修复,希望能够及时修复boost 1.45版本。 Boost-users:patch Boost-users:confirmation 感谢大家对此事的关注!

0

显然,答案是

使用以下代码,我在unordered_set上得到编译时警告和运行时断言(Visual C++ v10),而vector正常工作(除了最后一个元素中的空字符串,由于尾随'^')。

boost::unordered_set<std::string> tags_set;
vector<string> SplitVec; // #2: Search for tokens
boost::split( SplitVec, "a^b^c^", boost::is_any_of("^") ); 
boost::split( tags_set, "a^b^c^", boost::is_any_of("^") );

源(string)和目标容器之间的迭代器兼容性是问题所在。我想发布警告错误,但它是那些“战争与和平”模板警告之一。

编辑:

这似乎是 Boost unordered_set 中的一个错误?当我使用以下内容时,其表现如您所期望的那样:

std::unordered_set<std::string> tags_set_std;
boost::split( tags_set_std, string("a^b^c^"), boost::is_any_of(string("^")) );

谢谢 Steve。你使用的 Boost 版本是什么? - Jeremy Stribling

0

我认为答案应该是肯定的。

从头文件中可以看出(split.hppiter_find.hpp),split 函数的第一个参数是一个 SequenceSequenceT& Result,它会传递给 iter_split 函数,后者通过两个 boost::transform_iterator 对其进行范围构造:

SequenceSequenceT Tmp(itBegin, itEnd);
Result.swap(Tmp);
return Result;

所以这种类型所需要的就是一个构造函数,它接受一对迭代器,这些迭代器解引用为std::string(或者技术上来说是BOOST_STRING_TYPENAME)。并且有一个.swap()成员函数,还有一个SequenceSequenceT::iterator类型,其类型为std::string

证明:

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <string>
#include <iterator>
#include <algorithm>
#include <iostream>
struct X
{
   typedef std::iterator<std::forward_iterator_tag,
           std::string, ptrdiff_t, std::string*, std::string&>
           iterator;
   X() {}
   template<typename Iter> X(Iter i1, Iter i2)
   {
       std::cout << "Constructed X: ";
       copy(i1, i2, std::ostream_iterator<std::string>(std::cout, " " ));
       std::cout << "\n";
   }
   void swap(X&) {}
};
int main()
{
  X x;
  boost::split(x, "a^b^c^", boost::is_any_of(std::string(1, '^')));
}

我认为unordered_set<std::string>也应该满足这些要求。


感谢@Cubbi。您的结论是Boost 1.42中存在某个bug,并且@Steve在VisualC++/boost 1.44上看到的编译器警告是误导性的? - Jeremy Stribling
@Jeremy Stribling:这是我所期望的,因为我的测试和gcc的unordered_set的工作方式是一样的,而boost的不是,但他们可能有很好的理由。在这里等待更多答案并进行更多测试之前,我会先慎重称其为错误。 - Cubbi
@Jeremy - 看一下修改后的代码,我已经使用std::unordered_set代替了boost::unordered_set,现在可以正常工作了。 - Steve Townsend

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接