为啥 std::compare_three_way 不是模板结构/函子
Posted
技术标签:
【中文标题】为啥 std::compare_three_way 不是模板结构/函子【英文标题】:Why std::compare_three_way is not a template struct/functor为什么 std::compare_three_way 不是模板结构/函子 【发布时间】:2020-10-29 01:54:37 【问题描述】:例如std::less
的比较被定义为模板结构
template< class T = void >
struct less;
虽然std::compare_three_way
被定义为一个普通的结构,它的operator()
是一个模板函数。 (来自 MSVC 的代码)
struct compare_three_way
template <class _Ty1, class _Ty2>
requires three_way_comparable_with<_Ty1, _Ty2> // TRANSITION, GH-489
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
noexcept(noexcept(_STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right))) /* strengthened */
return _STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right);
using is_transparent = int;
;
那么为什么std::compare_three_way
不是模板结构呢?
template <class _Ty1, class _Ty2>
requires three_way_comparable_with<_Ty1, _Ty2>
struct compare_three_way
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const;
;
顺便说一句,我可以在我自己的容器实现中将std::less<T>
替换为std::three_way_compare
,比如C# 中的Comparer<T>
。
【问题讨论】:
您希望它成为类模板而不是使运算符模板化有什么特别的原因吗?还是您在问为什么不一致? @chris 在模板中使用,如std::less<T>
in std::set<T>
我认为 std::lessstd::less<T>
为您提供了一种能够比较 T
s 的类型。 std::compare_three_way
为您提供了一种能够比较 T
s 的类型。 (std::less<>
还为您提供了一种能够比较 T
s 的类型。)容器需要能够比较 T
s 的东西。这里有一个错误的假设。
【参考方案1】:
原std::less
(及其好友)的比较定义如下:
bool operator()( const T& lhs, const T& rhs ) const;
由于此函数调用运算符不是模板,它只能比较用于实例化std::less
模板的类型的对象。
在后来的语言修订版中,这些比较器通过专门化 std::less<>
进行了扩展,以支持通过对函数调用运算符本身进行模板化来比较不同类型的对象:
template< class T, class U>
constexpr auto operator()( T&& lhs, U&& rhs ) const
-> decltype(std::forward<T>(lhs) < std::forward<U>(rhs));
在大多数用例中,这在很大程度上淘汰了同类版本 (std::less<T>
),因为它要么是等效的,要么由于不强制转换为通用类型而更有效。保留旧的同构比较器以实现向后兼容性。
在提出std::compare_three_way
的时候,异构查找已经存在,所以从未引入同构版本。
在模板中使用它,比如
std::set<T>
中的std::less<T>
您可以在std::set<T>
和std::set<U>
等中使用std::compare_three_way
(象征性地),就像您可以使用std::less<>
一样。你(可能)不需要std::less<T>
也不需要std::less<U>
- 也不需要std::compare_three_way<T>
,它们都不存在。
【讨论】:
std::ranges::less
也不是类模板,尽管它最初是:P1291R0。【参考方案2】:
由于看起来std::compare_three_way
执行转发,当模板参数用于struct
时,它无法工作。 C++ Standard section 13.10.3.2 §3:
转发引用是对 cv 非限定模板参数的右值引用不代表类模板的模板参数(在类模板参数推导期间([over.match.class.deduct ]))。
因此,这里的_Ty1&&
和_Ty2&&
只是右值引用,无法正确转发:
template <class _Ty1, class _Ty2>
requires three_way_comparable_with<_Ty1, _Ty2>
struct compare_three_way
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const;
;
上面声明的模板结构只接受右值表达式作为其操作数,这不会太有用。让这个struct
可用的唯一方法是使参数const
引用。 MSVC 实现者选择了转发方式。
【讨论】:
以上是关于为啥 std::compare_three_way 不是模板结构/函子的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用 glTranslatef?为啥不直接更改渲染坐标?
为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?