c++ - 我如何告诉编译器 MyCustomType 是quality_comparable_with SomeOtherType?

标签 c++ c++20 c++-concepts

假设我有一个 MyCustomTypeSomeOtherType 比较:

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 中的异构查找算法:
error: no type named 'type' in 'struct std::common_reference<const MyCustomType&, const SomeOtherType&>'

我明白为什么会失败:我们可能有一个 operator== ,但我们不满足通用引用要求(另请参阅 Does `equality_­comparable_with` need to require `common_reference`? )。然而,实际上,我的类型是与 SomeOtherType 相当的平等。 .我如何说服编译器是这种情况?

最佳答案

在哲学上,std::equality_comparable_with 的共同引用要求显式编码编写异构时所做的隐式语句 operator==(T, U)实际上意味着相等*:有一些常见的父类(super class)型“T union U ”,其中 operator==是平等。这个“T union U ”实际上并不存在于 MyCustomType 的代码中和 SomeOtherType .如果我们通过特化使类型真正存在 std::common_reference_t ,然后我们可以见面std::equality_comparable_with .
*某些类型使用 operator==用于等价而不是相等(例如迭代器+哨兵),因此不应该也不符合 std::equality_comparable_with .

我们可以使用 std::basic_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).


为此,我们需要:
  • eq_proxy_ref<T>其作用类似于 T 的引用.
  • MyCustomType必须隐式转换为 eq_proxy_ref<T> .
  • SomeOtherType必须隐式转换为 eq_proxy_ref<T> .
  • basic_common_referenceMyCustomTypeSomeOtherType必须返回此 eq_proxy_ref<int> .一个 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 ,以及一些 Ranges 算法或 View 。因此,您的 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>;
    };
    }
    
    Compiler Explorer link
    我怀疑我错过了一些细微差别,但这足以满足std::equality_comparable_with .

    关于c++ - 我如何告诉编译器 MyCustomType 是quality_comparable_with SomeOtherType?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66944119/

    相关文章:

    c++ - 你能从 c++2a 中的函数返回基于范围的 View 吗?

    c++ - 不合格的名称查找

    c++ - 用 C++ 概念 TS 描述谓词

    C++20 概念 : Element iterable concept

    c++ - 如何编写可以采用 const 迭代器的 C++11 模板

    c++ - 立体视觉(3D 立体)会卷土重来吗?

    c++ - 为什么 C++ 分配器中没有重新分配功能?

    c++ - 检查类型是否/可转换为 c++20 中的范围

    c++ - unordered_map/unordered_set 中元组的通用哈希

    C++ 在 vector 中添加 double 元素