从哲学上讲,共同参考的要求std::equality_comparable_with
显式编码编写异构时所做的隐式语句operator==(T, U)
实际上意味着平等*:有一些共同的超类型“T
union U
” 其中operator==
是平等。这 ”T
union U
“实际上并不存在于代码中MyCustomType
and SomeOtherType
。如果我们通过专门化使该类型实际存在std::common_reference_t
,然后我们就可以见面了std::equality_comparable_with
.
*某些类型使用operator==
为了等价而不是相等(例如迭代器+哨兵),因此不应该也不满足std::equality_comparable_with
.
我们可以使用std::basic_common_reference https://en.cppreference.com/w/cpp/types/common_reference指定代理参考的自定义点:
类模板basic_common_reference
是一个定制点,允许用户影响结果common_reference
对于用户定义的类型(通常是代理引用)。
为此,我们需要:
-
eq_proxy_ref<T>
这就像一个参考T
.
-
MyCustomType
必须隐式转换为eq_proxy_ref<T>
.
-
SomeOtherType
必须隐式转换为eq_proxy_ref<T>
.
-
basic_common_reference
of MyCustomType
and SomeOtherType
必须返回这个eq_proxy_ref<int>
. A eq_proxy_ref<MyCustomProxy>
如果你想避免泄漏内部结构也可以工作MyCustomType
.
-
eq_proxy_ref<T>
其自身之间必须有比较运算符。
-
eq_proxy_ref<T>
必须遵守要求的精神。
但请注意,std::common_reference_t
不仅仅用于平等,包括std::three_way_comparable_with
, std::totally_ordered_with
,以及一些范围算法或视图。因此,您的eq_proxy_ref<T>
实际上应该是两种类型的共同引用,而不仅仅是一种实现平等的机制。
满足这些限制的示例如下:
#include <concepts>
#include <type_traits>
// Assuming you don't own SomeOtherType:
template <typename T>
class MyCustomTypeEqProxy {
template <typename>
friend class MyCustomTypeEqProxy;
private:
T ref_;
public:
template <typename U>
requires std::convertible_to<U, T>
constexpr MyCustomTypeEqProxy(U ref)
: ref_(ref)
{}
constexpr MyCustomTypeEqProxy(const SomeOtherType& rhs)
requires std::convertible_to<const int&, T>
: ref_(rhs.value)
{}
template <typename U>
requires std::equality_comparable_with<T, U>
constexpr bool operator==(const MyCustomTypeEqProxy<U>& rhs) const {
return ref_ == rhs.ref_;
};
};
struct MyCustomType {
int x;
constexpr bool operator==(const MyCustomType& rhs) const = default;
constexpr bool operator==(const SomeOtherType& rhs) const {
return x == rhs.value;
}
friend constexpr bool operator==(const SomeOtherType& lhs, const MyCustomType& rhs) {
return lhs.value == rhs.x;
}
constexpr operator MyCustomTypeEqProxy<int>() const { return MyCustomTypeEqProxy<int>(x); }
};
namespace std {
// May not be needed, but allows the custom proxy reference to expand to common references
// of what we're comparing against.
template <typename T, typename U, template <typename> class TQ, template <typename> class UQ>
struct basic_common_reference<::MyCustomTypeEqProxy<T>, U, TQ, UQ> {
using type = ::MyCustomTypeEqProxy< std::common_reference_t<T, UQ<U>> >;
};
template <typename T, typename U, template <typename> class TQ, template <typename> class UQ>
struct basic_common_reference<T, ::MyCustomTypeEqProxy<U>, TQ, UQ> {
using type = ::MyCustomTypeEqProxy< std::common_reference_t<TQ<T>, U> >;
};
// Tell std::common_reference_t about MyCustomTypeEqProxy
template <template <typename> class LQ, template <typename> class RQ>
struct basic_common_reference<::MyCustomType, ::SomeOtherType, LQ, RQ> {
using type = ::MyCustomTypeEqProxy<int>;
};
template <template <typename> class LQ, template <typename> class RQ>
struct basic_common_reference<::SomeOtherType, ::MyCustomType, LQ, RQ> {
using type = ::MyCustomTypeEqProxy<int>;
};
}
编译器资源管理器链接 https://gcc.godbolt.org/z/e75fGf74d
我怀疑我错过了一些细微差别,但这足以满足std::equality_comparable_with
.