为什么标准关联有序容器允许"const char*"作为其键?
Why do standard associative ordered containers allow `const char*` as their key?
据我所知,我们永远不应该使用关系运算符<>
比较两个常量字符串......因为它比较地址而不是值的事实:
const char* sz1 = "Hello";
const char* sz2 = "hello";
if(sz1 < sz2);// not valid. So use strcmp instead.
- 我注意到像
map, multimap, set, multiset
这样的有序关联容器对其key
施加了限制,以便应该将键与容器中的元素进行排序进行比较。键的默认运算符是<
运算符。
一切都很清楚,直到我创建了const char*
map, set
然后我得到的结果不正确:
std::set<const char*> scp{ "Hello", "World", "C++", "Programming" };
std::set<std::string> sstr{ "Hello", "World", "C++", "Programming" };
// error
std::copy(scp.cbegin(), scp.cend(), std::ostream_iterator<const char*>(std::cout, " "));
std::cout << std::endl;
// Ok
std::copy(sstr.cbegin(), sstr.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
很明显,
scp
比较字符串的指针,而只要类string
定义了正常工作<
sstr
就可以了。为什么STL允许这样做?(创建关键元素类型为
char*
的关联容器(为什么这里甚至没有警告?
键的默认运算符是
<
运算符。
这不是真的。 非散列关联容器的默认比较运算符为std::less
。std::less
使用operator <
进行比较,但有一个关键区别。 与内置的指针不同operator <
两个指针都不需要大于另一个指针。
源
std::less
有
任何指针类型的专用化都会产生一个严格的总顺序,该顺序在这些专用化之间是一致的,并且也与内置运算符
<
、>
、<=
、>=
强加的偏序一致。
源
因此,这是一个安全的操作,我们可以可靠地将指针存储在地图中。
正如其他人指出的那样,也许有时你想要指针比较,如果你不这样做,那么容器允许你提供自己的自定义比较运算符,如下所示:
#include <cstring>
#include <iostream>
#include <iterator>
#include <string>
#include <set>
struct CStrCmp {
bool operator() (const char* lhs, const char* rhs) const {
return strcmp(lhs, rhs) < 0;
}
};
int main()
{
std::set<const char*, CStrCmp> scp{ "Hello", "World", "C++", "Programming" };
std::set<std::string> sstr{ "Hello", "World", "C++", "Programming" };
// This works too now
std::copy(scp.cbegin(), scp.cend(), std::ostream_iterator<const char*>(std::cout, " "));
std::cout << std::endl;
// Ok
std::copy(sstr.cbegin(), sstr.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
}