实例化map_aligner<string,string>触发std::logic_error错误排查
C++模板类初始化引发
std::logic_error错误排查与修复 我编写了一个自定义模板类头文件mapalign.h,用于对齐多个std::map的键值对。但运行代码时触发错误:'std::logic_error' what(): basic_string::_M_construct null not valid,定位到错误由map_aligner<std::string, std::string> lsa;这行代码引发,不清楚错误原因及解决方法。
源文件代码
#include <iostream> #include "mapalign.h" #include <string> #include <algorithm> #include <map> #include "mapalign.h" struct string_size_less { bool operator()( const std::string& a, const std::string& b ) const { return a.size() < b.size(); } }; const int max = 1000; bool check() { std::map<std::string, int> sma; std::map<std::string, int> smb; std::map<std::string, int> smc; map_aligner<std::string, int> sa; sa.add( sma ); sa.add( smb ); sa.add( smc ); sma[ "C++" ] = 1; smb[ "Ada" ] = 2; smc[ "C" ] = 3; smc[ "Python" ] = 4; smc[ "Ada"] = 5; sa.align(); std::map<int, double> ima; std::map<int, double> imb; for( int i = 0; i < max; ++i ) { if ( 0 == i % 2 ) { ima[ i ] = max - i; } else { imb[ i ] = max; } } map_aligner<int, double> ia; ia.add( ima ); ia.add( imb ); ia.align(); if ( ( 4 != sma.size() && 1 != imb.count( 0 )) || max * 1U != ima.size() || 1 != smc.count( "C++" ) || "Ada" != sma.begin()->first || 0 != sma.begin()->second || 4 != smc.size() || 1 != smb.count( "Python" ) || 0 != imb.begin()->first || 0.8 <= imb.begin()->second || 1 != imb.count( max / 2 ) ) { return false; } sma[ "Pascal" ] = 5; sa.set_filler( max ); sa.align(); std::map<std::string, std::string> langsa; langsa[ "C++" ] = "<3"; langsa[ "Python" ] = ":|"; std::map<std::string, std::string> langsb; langsb[ "Brainfuck" ] = ":S"; langsb[ "Python" ] = ":/"; langsb[ "C" ] = ":)"; map_aligner<std::string, std::string> lsa; lsa.add( langsa ); lsa.add( langsb ); lsa.align(); lsa.erase( "Python" ); if ( 0 != langsa.count( "Python" ) || max != smc[ "Pascal" ] || !langsa.begin()->second.empty() || max != smb[ "Pascal" ] || 2 * 1U != langsb.begin()->second.size() || 0 != langsb.count( "Python" ) || 1 != langsb.count( "C++" ) ) { return false; } std::map<std::string, std::string, string_size_less> lma; std::map<std::string, std::string, string_size_less> lmb; std::map<std::string, std::string, string_size_less> lmc; lma[ "C++" ] = ":D"; lmb[ "Eiffel" ] = ":P"; lmc[ "C" ] = "8-)"; lmc[ "Ada" ] = "!"; map_aligner<std::string, std::string, string_size_less> ls; ls.add( lma ); (ls += lmb) += lmc; ls.align(); std::cout << (3 == ls.count()) << (1 == lmc.count( "Ada" )) << (3 * 1U == lmb.size()) << (1 == lma.count( "Python" )) << (2 == lsa.count()) << (2 == ia.count()) ; return ( 3 == ls.count() && 1 == lmc.count( "Ada" ) && 3 * 1U == lmb.size() && 1 == lma.count( "Python" ) && 2 == lsa.count() && 2 == ia.count() ); } int main() { std::cout << "Your solution is " << (check() ? "" : "not ") << "ready for submission." << std::endl; }
头文件代码
#ifndef MAPALIGN_H #define MAPALIGN_H #include <vector> #include <map> #include <typeinfo> template<typename KEY, typename VALUE, typename COMPARE = std::less<KEY>> class map_aligner{ public: typedef typename std::map<KEY, VALUE, COMPARE>::size_type size_type; map_aligner() {} void add(std::map<KEY, VALUE, COMPARE>& m) { maps_.push_back(&m); } map_aligner<KEY, VALUE, COMPARE> operator+=(std::map<KEY, VALUE, COMPARE>& m){ return maps_.push_back(&m); } size_type count() const { return maps_.size(); } void align(){ for(int i = 0; i != (int)maps_.size(); i++ ){ std::map<KEY, VALUE>* first_map = maps_.at(i); for(typename std::map<KEY, VALUE>::iterator j = first_map->begin(); j !=first_map->end(); j++ ){ for(int h = 0; h != (int)maps_.size(); h++ ){ std::map<KEY, VALUE>* second_map = maps_.at(h); if(first_map != second_map && notContainsPair(second_map, j)){ second_map->insert(std::make_pair(j->first, filler_)); } } } } } bool notContainsPair(std::map<KEY, VALUE>* map,typename std::map<KEY, VALUE>::iterator it){ for(typename std::map<KEY, VALUE>::iterator g = map->begin(); g != map->end(); g++ ){ if(it->first != g->first){ return true; } } return false; } void set_filler(VALUE filler){ filler_ = filler; } void erase(KEY key){ for(int h = 0; h != (int)maps_.size(); h++ ){ std::map<KEY, VALUE>* map = maps_.at(h); typename std::map<KEY, VALUE>::iterator it; if(typeid(it->first) != typeid(it->first)){ map->erase(key); } } } private: std::vector<std::map<KEY, VALUE, COMPARE>*> maps_; VALUE filler_ = (VALUE) NULL; }; #endif
错误原因
- filler_初始化非法:头文件中
VALUE filler_ = (VALUE) NULL;,当VALUE为std::string时,将NULL(本质是0)强制转换为std::string,会触发字符串构造函数尝试从空指针创建对象,这是未定义行为,直接抛出std::logic_error。 - notContainsPair逻辑完全错误:该函数遍历map时,只要遇到一个键不等于目标键就返回true,实际应该是遍历完所有键都不存在才返回true,当前逻辑会导致错误插入大量无效键值对。
- operator+=返回值错误:
push_back是void类型,无法返回map_aligner对象,会导致编译报错。 - erase函数逻辑无效:
if(typeid(it->first) != typeid(it->first))永远为false,erase操作从未执行。
修复方案
1. 修正filler_初始化
将类内的VALUE filler_ = (VALUE) NULL;移除,改为在构造函数中用值初始化语法生成合法默认值:
map_aligner() : filler_(VALUE{}) {}
这样对于std::string会初始化为空字符串,数值类型会初始化为0,符合各类型的合法默认值规则。
2. 修复notContainsPair函数
直接使用map::find方法判断键是否存在,更高效且逻辑正确:
bool notContainsPair(std::map<KEY, VALUE, COMPARE>* map, typename std::map<KEY, VALUE, COMPARE>::iterator it){ return map->find(it->first) == map->end(); }
3. 修正operator+=返回值
改为返回*this,支持链式调用:
map_aligner<KEY, VALUE, COMPARE>& operator+=(std::map<KEY, VALUE, COMPARE>& m){ maps_.push_back(&m); return *this; }
4. 修复erase函数
移除无效的typeid判断,直接执行erase:
void erase(KEY key){ for(auto* map : maps_){ map->erase(key); } }
5. 修正align函数的类型匹配问题
align函数中统一使用std::map<KEY, VALUE, COMPARE>类型,同时改用范围for循环简化代码:
void align(){ for(auto* first_map : maps_){ for(const auto& pair : *first_map){ for(auto* second_map : maps_){ if(first_map != second_map && second_map->find(pair.first) == second_map->end()){ second_map->insert(std::make_pair(pair.first, filler_)); } } } } }
内容的提问来源于stack exchange,提问作者programmerGirl82
相关产品推荐
相关产品推荐

