如何告诉编译器MyCustomType与SomeOtherType是equal_comparable_with? [英] How can I tell the compiler that MyCustomType is equality_comparable_with SomeOtherType?

查看:62
本文介绍了如何告诉编译器MyCustomType与SomeOtherType是equal_comparable_with?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个 MyCustomType SomeOtherType 进行了比较:

Suppose I have a MyCustomType that has comparisons with SomeOtherType:

struct SomeOtherType {
    int value;

    constexpr bool operator==(const SomeOtherType& rhs) const = default;
};

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;
    }
};

这很好,但是 static_assert(std :: equality_comparable_with< MyCustomType,SomeOtherType>); 失败,这意味着我不能在 std :: ranges中将它们用于异构查找算法:

This is great, but static_assert(std::equality_comparable_with<MyCustomType, SomeOtherType>); fails, meaning that I can't use them for heterogenous lookup in the std::ranges algorithms:

error: no type named 'type' in 'struct std::common_reference<const MyCustomType&, const SomeOtherType&>'

我了解失败的原因:我们可能有一个 operator == ,但是我们不满足通用参考要求(另请参见).但是,实际上,我的类型与 SomeOtherType 相等.我如何说服编译器是这种情况?

I understand why this fails: we may have an operator==, but we don't meet the common-reference requirements (see also Does `equality_­comparable_with` need to require `common_reference`? ). However, my type is, practically speaking, equality comparable with SomeOtherType. How can I convince the compiler that this is the case?

推荐答案

从哲学上讲, std :: equality_comparable_with 的公共引用要求是对编写异类时所做的隐式声明进行显式编码.运算符==(T,U)实际上表示相等*:有一些常见的超类型" T union U "为此, operator == 是相等的.此" T 联合 U "在 MyCustomType SomeOtherType 的代码中实际上并不存在.如果我们通过专门化 std :: common_reference_t 来使类型实际存在,那么我们就可以满足 std :: equality_comparable_with .

Philosophically, the common-reference requirements of std::equality_comparable_with are explicitly encoding the implicit statement made when writing a heterogenous operator==(T, U) to actually mean equality*: there is some common supertype "T union U" for which the operator== is equality. This "T union U" doesn't actually exist in the code for MyCustomType and SomeOtherType. If we make the type actually exist by specializing std::common_reference_t, then we can meet std::equality_comparable_with.

*某些类型使用 operator == 来实现等价而不是相等(例如,迭代器+前哨),因此不应该并且也不满足 std :: equality_comparable_with .

*Some types use operator== for equivalence rather than equality (e.g. iterators+sentinels), and as such should not and do not meet std::equality_comparable_with.

我们可以使用 std :: basic_common_reference 用于指定代理引用的自定义点:

We can use the std::basic_common_reference customization point to specify a proxy reference:

类模板 basic_common_reference 是一个自定义点,允许用户影响用户定义类型(通常是代理引用)的 common_reference 的结果.

The class template basic_common_reference is a customization point that allows users to influence the result of common_reference for user-defined types (typically proxy references).

要使其正常工作,我们需要:

For this to work, we need:

  • eq_proxy_ref< T> ,其作用类似于 T 的引用.
  • MyCustomType 必须可以隐式转换为 eq_proxy_ref< T> .
  • SomeOtherType 必须隐式转换为 eq_proxy_ref< T> .
  • MyCustomType
  • basic_common_reference SomeOtherType 必须返回此 eq_proxy_ref< int> .如果您想避免泄漏 MyCustomType 的内部信息,则 eq_proxy_ref< MyCustomProxy> 也可以工作.
  • eq_proxy_ref< T> 之间必须具有比较运算符.
  • eq_proxy_ref< T> 必须遵守要求的精神.
  • eq_proxy_ref<T> which acts like a reference for T.
  • MyCustomType must be implicitly convertible to eq_proxy_ref<T>.
  • SomeOtherType must be implicitly convertible to eq_proxy_ref<T>.
  • basic_common_reference of MyCustomType and SomeOtherType must return this eq_proxy_ref<int>. A eq_proxy_ref<MyCustomProxy> could work too if you wanted to avoid leaking the internals of MyCustomType.
  • eq_proxy_ref<T> must have comparison operators between itself.
  • eq_proxy_ref<T> must obey the spirit of the requirement.

满足这些约束的示例如下:

An example meeting these constraints follows:

#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>;
};
}

编译器资源管理器链接

我怀疑我错过了一些细微差别,但这足以满足 std :: equality_comparable_with .

I suspect that I missed some nuances, but this is enough to meet std::equality_comparable_with.

这篇关于如何告诉编译器MyCustomType与SomeOtherType是equal_comparable_with?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆