我有以下代码行:
const char *values[] = { "I", "We", "You", "We"};
std::set<const char*> setValues;
for( int i = 0; i < 3; i++ ) {
const char *val = values[i];
std::set<const char*>::iterator it = setValues.find( val );
if( it == setValues.end() ) {
setValues.insert( val );
}
else {
cout << "Existing value" << endl;
}
}
有了这个,我试图在set
中插入非重复值,但不知何故代码没有打印现有元素的打印,并且插入了重复值。
这里有什么问题?
答案 0 :(得分:3)
你应该为const char *定义更少的谓词,并传入set模板,使set对象与指针一起正常工作:
struct cstrless {
bool operator()(const char* a, const char* b) {
return strcmp(a, b) < 0;
}
};
std::set<const char*, cstrless> setValues;
答案 1 :(得分:3)
std::set<T>::find
使用<
类型的默认运算符T
。
您的类型为const char*
。这是指向内存中地址的指针,因此find
方法只是将给定字符串的内存中的地址与来自set
的所有字符串的内存中的地址进行比较。这些地址对于每个字符串都是不同的(除非编译器对其进行优化)。
您需要告诉std::set
如何正确比较字符串。我可以看到AnatolyS已经在他的回答中写了如何做到这一点。
答案 2 :(得分:0)
除非您使用自定义比较功能对象,否则std::set
默认使用operator<(const key_type&,key_type&)
。如果且仅当它们指向同一个对象时,两个指针是相等的。
以下是三个对象的示例:
char a[] = "apple";
char b[] = "apple";
const char (&c)[6] = "apple"
前两个是数组,第三个是左值引用,它绑定到也是数组的字符串文字对象。作为独立的对象,他们的地址当然也不同。所以,如果你要写:
setValues.insert(a)
bool is_in_map = setValues.find("apple") != setValues.end();
is_in_map
的值为false
,因为该集合仅包含a
中字符串的地址,而不包含字面值中字符串的地址 - 即使字符串的内容相同。
解决方案:不要使用operator<
来比较指向c字符串的指针。请改用std::strcmp
。使用std::set
,这意味着使用自定义比较对象。但是,你还没有做过警告。只要字符串中的键指向它们,您仍必须确保字符串保留在内存中。例如,这将是一个错误:
char a[] = "apple";
setValues.insert(a);
return setValues; // oops, we returned setValues outside of the scope
// but it contains a pointer to the string that
// is no longer valid outside of this scope
解决方案:注意范围,或只使用std::string
。
(这个答案抄袭了我自己关于std::map
here的答案)