为什么 std::不如指针的 "<" 好?
Why is std::less better than "<" for pointers?
C++ primer, 5th, 14.8.2, 将库函数对象与算法一起使用:
vector<string *> nameTable; // vector of pointers
// error: the pointers in nameTable are unrelated, so < is undefined
sort(nameTable.begin(), nameTable.end(),
[](string *a, string *b) { return a < b; });
// ok: library guarantees that less on pointer types is well defined
sort(nameTable.begin(), nameTable.end(), less<string*>());
然后我检查了libc ++中的std::less
实现(为提高可读性而修改了代码块):
template <class _Tp>
struct less {
constexpr bool operator()(const _Tp& __x, const _Tp& __y) const
{return __x < __y;}
};
我发现std::less
也使用<
来完成工作,那么为什么<
未定义指针而std::less
没有呢?我为什么要使用std::less
?
因为<
并不总是operator<()
.只有类具有运算符函数,因此您的建议不适用于内置类型。
此外,指针上的<
不一定实现严格-弱排序,而std::less
(通过专业化 - 您发布的内容不是"全部"std::less
!)需要:
对于任何指针类型,std::less 的专用化都会产生严格的总顺序<即使内置运算符没有。>
简而言之:std::less
适用于任何支持小于比较的东西。
为什么我们需要std::less
?
仅当指针是同一数组的一部分时,指针之间的比较才得到明确定义。否则,结果是未指定的(请参阅 [expr.rel])。 请注意,这与未定义的行为不同。
这在对指针数组进行排序、保持std::set<void*>
等时是有问题的,因为算法和数据结构需要 [严格的弱排序]。 如果某些指针是无序的,这只是部分排序。
std::less
保证了严格的总序,这甚至比严格的弱序还要强:
对于模板
less
、greater
、less_equal
和greater_equal
,任何指针类型的专用化都会产生与实现定义的指针严格总顺序一致的结果。[注意:如果
a < b
为P
类型的指针a
和b
明确定义,则(a < b) == less<P>()(a, b)
、(a > b) == greater<P>()(a, b)
等。 —尾注]
- [比较.一般]
在具有内存分段的体系结构上,这可能会使std::less
更加昂贵,因为需要比较段内的基本指针和偏移量。 如果<
只提供部分排序,并且开发人员选择std::less
,如果他们确实需要全部订购,则可能会更有效率。
为什么在libc++中,std::less
被简单地委托给<
?
引用的段落还暗示了libc++使用<
实现std::less
的原因:因为无论如何<
恰好为Clang定义了很好的定义,所以它的标准库不需要做任何特别的事情。
但是,标准并不能保证这一点,并且并非每个编译器都向<
运算符提供如此强大的保证。GCC 不提供<
的总订单,其标准库使用以下方式实现std::less
:
// for two pointers x, y
return (std::uintptr_t)x < (std::uintptr_t)y;
总顺序是通过比较指针表示的内存地址而不是直接比较指针来实现的。
比较std::less
的实现
Library | Header | |
---|---|---|
style="text-align: center;">GCC libstdc++ | bits/stl_function.h | > |
style="text-align: center;">Clang libc++ | __functional/operations.h<</strong>td style="text-align: center;"> | >|
style="text-align: center;">MSVC STL | type_traits<</strong>td style="text-align: center;"> | >
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 错误:调用"std::vector<:vector<int>>::p ush_back(std::vector<std::__cxx11::basic_string<
- C 建造者Clang STD :: Sill,找不到超载的操作员&lt;
- 为什么STD :: MAP需要操作员&lt;以及我如何写一个
- std::vector::reserve(未知m),我知道m<<;N(通常)并且知道N
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- C++运算符<<调用::ostream而不是std::osttream
- 是std :: set&lt; std :: future&gt;不可能存在
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- BOOST ::变体无法解决运算符&lt;&lt;对于STD :: Ostream
- C++重载<<具有typedef'd std::vector
- 以x的倍数填充前导零,使用std::cout<<std::十六进制
- 错误:没有匹配'运算符<<"在'std::cout
- std::pair的默认构造函数<>将基本类型(int等)设置为零
- std::ostream&operator< & lt; (std:: ostream&压力,压力& &;val)
- 将std::endl传递给std::operator<<
- std::映射<>或std::vector<>在处理大型标志集时
- 重载& lt; & lt;使用命名空间std