【问题标题】:Named requirement for std::tuple-like? [duplicate]std::tuple-like 的命名要求? [复制]
【发布时间】:2019-01-08 21:50:34
【问题描述】:

我正在为std::tuple 制作一个通用哈希算法,并意识到我也可以让它适用于std::pair 和std::array,因为这三个类实现了std::tuple_size 和std::get。这在任何地方都“标准化”了吗?是否有一个“std::allocator_traits”但对于 std::tuple-like 类而不是 std::allocator-like 类?

目前我这样做是为了检查一个类是否“类似元组”:

#include <type_traits>
#include <cstddef>
#include <utility>
#include <tuple>

namespace is_tuple {
    namespace detail {
        // T* exists for incomplete types, but not sizeof(T)
        template<class T, ::std::size_t = sizeof(T)>
        ::std::true_type is_complete_type_impl(T*);

        ::std::false_type is_complete_type_impl(...);

        template<class T>
        struct is_complete_type : decltype(
            is_complete_type_impl(::std::declval<T*>())
        ) {};
    }

    template<class T>
    struct is_tuple : detail::is_complete_type<::std::tuple_size<T>>::type {};
}

这可能有一些误报(虽然我不认为它有)

这些是我对“类元组”类型、T 类型和称为t 的T 类型对象的“要求”:

  1. std::tuple_size&lt;T&gt; 是一个完整的类型
  2. std::tuple_size&lt;T&gt;::value 是 static constexpr const std::size_t(称为 N)
  3. std::get&lt;I&gt;(t) 为 [0, N) 中的所有 I 返回非 void

(这是否正确完整?我也实现了检查std::get,但代码太长,无法发布)

此外,除了专门化 std::tuple_size&lt;T&gt; (可能是 iterator_tag 之类的东西)之外,是否有更好的方法来指定类型是否是“类元组”

【问题讨论】:

  • 检查std::get&lt;i&gt; 应该检查get&lt;i&gt;,因为应通过ADL 找到候选人
  • std::tuple_element 可能会丢失(例如,结构化绑定需要)(并且允许处理引用类型:std::get&lt;0&gt;(std::tie(lvalue)) 和 std::get&lt;0&gt;(std::make_tuple(lvalue)) 都返回引用类型)。
  • @GuillaumeRacicot: t.get&lt;I&gt;() 也可以用于structured_binding。

标签: c++ tuples stdtuple


【解决方案1】:

这在任何地方都“标准化”了吗?

如果您指的是哈希算法,那么(就像很多事情一样)boost 中已经有一个完整的解决方案。

boost 当然,可以说更加标准化并且肯定更便携比 c++ 标准本身的各种实现。1

#include <boost/functional/hash.hpp>
#include <tuple>
#include <utility>
#include <array>
#include <string>
#include <iostream>

template<class T>
std::size_t hashit(T const& x)
{
    using x_type = std::decay_t<T>;
    return boost::hash<x_type>()(x);
}

template<class T>
std::size_t hashit2(T const& x)
{
    using boost::hash_value;
    return hash_value(x);
}

int main()
{
    using namespace std::literals;

    std::cout << hashit(std::make_tuple(1, 2, 5, "foo"s)) << '\n';
    std::cout << hashit2(std::make_tuple(1, 2, 5, "foo"s)) << '\n';

    std::cout << '\n';

    std::cout << hashit(std::make_pair(1.0, 2.0)) << '\n';
    std::cout << hashit2(std::make_pair(1.0, 2.0)) << '\n';

    std::cout << '\n';

    std::cout << hashit(std::array<int, 3>{4, 5, 6}) << '\n';
    std::cout << hashit2(std::array<int, 3>{4, 5, 6}) << '\n';
}

1 这将被许多人视为一个有争议的声明。尽管如此,它在经验上是正确的。

【讨论】:

  • 那么我怎样才能让它与我自己的班级一起工作呢?只需在同一个命名空间中定义get 和std::tuple_size?
  • 答案完全错过了OP的问题。他对自己的哈希实现很满意。他想知道如何获取/提供元组式“特征”。
  • @Artyer 如果“工作”是指“产生合理的哈希值”,那么您只需在与您的类相同的命名空间中为您的类编写hash_value() 的重载。其余的都由 Boost 完成。您可以将hash_value() 写成hash_value()、boost::hash&lt;&gt;、boost::hash_combine() 等等。 boost.org/doc/libs/1_69_0/doc/html/hash/custom.html
  • @davidbak 人们开始为元组和容器编写通用哈希算法的通常原因是(a)c++ 令人震惊的标准让他们在这个主题上高高在上,并且(b)因为他们没有没有意识到boost的好人已经以最出色的方式来救援了。
  • @RichardHodges - 我相信你是对的。从我自己的经验中我也知道,在处理其他问题时,您有时会独立地遇到您想知道的其他问题。也就是说,即使您需要知道它来解决原始问题,如果有人提出了原始问题的替代解决方案,您仍然想要知道。而这个 OP 显然想知道元组特征,这是这个问题的全部焦点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
  • 2019-06-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多