为什么我的"choose k from n"算法适用于 std::vector,而不适用于 std::map?
Why does my "choose k from n" algorithm work with std::vector but not with std::map?
我需要一种迭代器,使我可以迭代容器中的所有元素组合(允许重复和{1,2}=={2,1}
)。我写了这篇文章:
#include <iostream>
#include <map>
#include <vector>
template <typename T>
struct ChooseKfromN{
T mbegin;
T mend;
std::vector<T> combo;
ChooseKfromN(T first,T last,int k) : mbegin(first),mend(last),combo(k,first) {}
bool increment(){
for (auto it = combo.begin();it!=combo.end();it++){
if (++(*it) == mend){
if (it != combo.end()){
auto next = it;
next++;
auto nexit = (*next);
nexit++;
std::fill(combo.begin(),next,nexit);
}
} else { return true;}
}
std::cout << "THIS IS NEVER REACHED FOR A MAP !! n" << std::endl;
return false;
}
typename std::vector<T>::const_iterator begin(){ return combo.begin();}
typename std::vector<T>::const_iterator end() { return combo.end();}
};
template <typename T>
ChooseKfromN<T> createChooseKfromN(T first,T last,int k) {
return ChooseKfromN<T>(first,last,k);
}
在 std::map
...
int main(){
//std::vector<std::string> xx = {"A","B","C"};
std::map<int,int> xx;
xx[1] = 1;
xx[2] = 2;
auto kn = createChooseKfromN(xx.begin(),xx.end(),2);
int counter = 0;
do {
for (auto it = kn.begin();it != kn.end();it++){
std::cout << (**it).first << "t";
}
std::cout << "n";
counter++;
} while(kn.increment());
std::cout << "counter = " << counter << "n";
}
...我遇到了一个运行时错误。在使用矢量时,我得到了正确的输出(另请参见此处):
A A
B A
C A
B B
C B
C C
THIS IS NEVER REACHED FOR A MAP !!
counter = 6
当它与std::vector
一起使用时,为什么std::map
破裂?
valgrind Reports
==32738== Invalid read of size 8
==32738== at 0x109629: ChooseKfromN<std::_Rb_tree_iterator<std::pair<int const, int> > >::increment() (42559588.cpp:17)
==32738== by 0x108EFE: main (42559588.cpp:43)
==32738== Address 0x5a84d70 is 0 bytes after a block of size 16 alloc'd
==32738== at 0x4C2C21F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32738== by 0x10B21B: __gnu_cxx::new_allocator<std::_Rb_tree_iterator<std::pair<int const, int> > >::allocate(unsigned long, void const*) (new_allocator.h:104)
==32738== by 0x10B113: std::allocator_traits<std::allocator<std::_Rb_tree_iterator<std::pair<int const, int> > > >::allocate(std::allocator<std::_Rb_tree_iterator<std::pair<int const, int> > >&, unsigned long) (alloc_traits.h:416)
查看第17行,在这里:
if (it != combo.end()){
auto next = it;
next++;
auto nexit = (*next); // Line 17
nexit++;
如果next
== combo.end()
,则*next
是无效的参考。
使用位图方法修复了代码(请参阅https://stackoverflow.com/a/a/28714822/2015579)
我维护了2个向量,一个是启用索引的位图,我们可以计入,另一个是对容器中项目的引用的向量。
我还提供了一个模板的emit()
功能,因此相同的通用代码可以在地图,向量,集合,unordered_maps等上运行。
#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <iterator>
template <typename Iter>
struct ChooseKfromN
{
using iterator_type = Iter;
using value_type = typename std::iterator_traits<iterator_type>::value_type;
using result_type = std::vector<std::reference_wrapper<const value_type>>;
result_type values_;
std::vector<bool> bitset_;
int k_;
ChooseKfromN(Iter first,Iter last,int k)
: values_(first, last)
, bitset_(values_.size() - k, 0)
, k_(k)
{
std::reverse(values_.begin(), values_.end());
bitset_.resize(values_.size(), 1);
}
result_type& get(result_type& target) const
{
target.clear();
for (std::size_t i = bitset_.size() ; i ; ) {
--i;
if (bitset_[i]) {
target.push_back(values_[i]);
}
}
return target;
}
bool increment(){
return std::next_permutation(bitset_.begin(), bitset_.end());
}
};
template <typename T>
ChooseKfromN<T> createChooseKfromN(T first,T last,int k) { return ChooseKfromN<T>(first,last,k); }
template<class T>
std::ostream& emit_key(std::ostream& os, const T& t)
{
return os << t;
}
template<class K, class V>
std::ostream& emit_key(std::ostream& os, const std::pair<const K, V>& kv)
{
return os << kv.first;
}
template<class Container>
void test(Container const& c)
{
auto kn = createChooseKfromN(c.begin(),c.end(),2);
using ChooserType = decltype(kn);
using ResultType = typename ChooserType::result_type;
int counter = 0;
ResultType result_buffer;
do {
kn.get(result_buffer);
for (auto&& ref : result_buffer)
{
emit_key(std::cout, ref.get()) << 't';
}
std::cout << "n";
counter++;
} while(kn.increment());
std::cout << "counter = " << counter << "n";
}
int main(){
std::map<int,int> xx;
xx[1] = 1;
xx[2] = 2;
xx[3] = 3;
xx[4] = 4;
std::vector<std::string> yy = {"A","B","C"};
test(xx);
test(yy);
}
预期输出:
1 2
1 3
2 3
1 4
2 4
3 4
counter = 6
A B
A C
B C
counter = 3
相关文章:
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 结构化绑定是否适用于 std::vector?
- 没有适用于 std::unique_ptr 的适当默认构造函数
- 适用于 std::unique_ptr 的内存高效自定义删除器?
- std::string 是否仅适用于 c++ 中的'std::cin'
- 为什么不区分大小写适用于 std::unordered_set的 std::hash 函数?
- 为什么我的"choose k from n"算法适用于 std::vector,而不适用于 std::map?
- 无法获得 boost::spirit parser&lexer 适用于 std::string 或 int 或 double 以外的令牌类型
- 重载分辨率如何适用于 std::vector:<int>:insert
- 如何将上下文信息传递给自定义运算符<<适用于 std::iostream
- 在C++std::streams中,失败后,如何获得失败原因?必需:线程安全,适用于Windows和Linux(或至少M
- 适用于 MacOS 的"-std=gnu++0x"选项
- 警告:扩展初始值设定项列表仅适用于-std=c++0x或-std=gnu++0x
- 警告:扩展初始化器列表仅适用于STD c++ 11
- 自定义迭代器适用于std::sort,但不适用于tbb::parallel_sort
- 不在异常中嵌入std::字符串的规则是否仍然适用于move构造函数
- boost::variant是否适用于std::string
- 如何包装对"std::thread"构造函数的调用?(适用于gcc、VS和icpc)
- 可变参数模板转换为 std::function<R(ARGS...)>适用于 GCC 而不是 MSVC2013,为什么?