【问题标题】:using C++ tuple types as key in a map使用 C++ 元组类型作为映射中的键
【发布时间】:2014-10-02 14:16:59
【问题描述】:

我正在尝试将不同类型的值集合映射到连续空间中。为此(有效地)我需要知道所有元素的总大小和每个元素的偏移量。如果集合作为元组给出,则获取总大小很容易。它还允许预先计算偏移量。我很难按类型获得偏移量。为了简化示例,我假设 Tuple 中的类型将是唯一的(实际上 Tuple 将具有一对唯一标签 + 非唯一值)。这是我当前的非编译尝试:

#include <cstddef>
#include <iostream>
#include <tuple>

struct A
{
    std::size_t size()
    {
        return 3;
    }
};

struct B
{
    std::size_t size()
    {
        return 2;
    }
};

struct C
{
    std::size_t size()
    {
        return 4;
    }
};

template <typename Tuple>
struct Foo
{
    const Tuple& tuple_;
    std::array<int, std::tuple_size<Tuple>::value> array_;

    Foo(const Tuple& tuple) : tuple_(tuple)
    {
        std::cout << init() << '\n';
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0>
    typename std::enable_if<std::tuple_size<Tuple>::value == INDEX, std::size_t>::type
    init()
    {
        return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0>
    typename std::enable_if<std::tuple_size<Tuple>::value != INDEX, std::size_t>::type
    init()
    {
        auto offset = init<INDEX + 1>();

        std::cout << "index: "<< INDEX << "; offset: " << offset << '\n';

        array_[INDEX] = offset;

        return offset + std::get<INDEX>(tuple_).size();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0, typename T>
    typename std::enable_if<std::tuple_size<Tuple>::value == INDEX, std::size_t>::type
    offset(const T&)
    {
        return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0, typename T>
    typename std::enable_if<(std::tuple_size<Tuple>::value != INDEX) && !std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value, std::size_t>::type
    offset(const T& t)
    {
        return offset<INDEX + 1>(t);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    template <std::size_t INDEX = 0, typename T>
    typename std::enable_if<(std::tuple_size<Tuple>::value != INDEX) && std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value, std::size_t>::type
    offset(const T&)
    {
        return array_[INDEX];
    }    
};

int main()
{
    A a;
    B b;
    C c;
    auto t = std::tie(a, b, c);
    using T = decltype(t);
    Foo<T> foo(t);

    std::cout << foo.offset(a) << '\n';
    //std::cout << foo.offset(b) << '\n';
    //std::cout << foo.offset(c) << '\n';
}

编译错误:

In file included from prog.cpp:3:0:
/usr/include/c++/4.8/tuple: In instantiation of ‘struct std::tuple_element<1u, std::tuple<C&> >’:
/usr/include/c++/4.8/tuple:680:12:   recursively required from ‘struct std::tuple_element<2u, std::tuple<B&, C&> >’
/usr/include/c++/4.8/tuple:680:12:   required from ‘struct std::tuple_element<3u, std::tuple<A&, B&, C&> >’
prog.cpp:79:22:   recursively required from ‘typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type Foo<Tuple>::offset(const T&) [with unsigned int INDEX = 1u; T = A; Tuple = std::tuple<A&, B&, C&>; typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type = unsigned int]’
prog.cpp:79:22:   required from ‘typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type Foo<Tuple>::offset(const T&) [with unsigned int INDEX = 0u; T = A; Tuple = std::tuple<A&, B&, C&>; typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type = unsigned int]’
prog.cpp:101:27:   required from here
/usr/include/c++/4.8/tuple:680:12: error: invalid use of incomplete type ‘struct std::tuple_element<0u, std::tuple<> >’
     struct tuple_element<__i, tuple<_Head, _Tail...> >
            ^
In file included from /usr/include/c++/4.8/tuple:38:0,
                 from prog.cpp:3:
/usr/include/c++/4.8/utility:84:11: error: declaration of ‘struct std::tuple_element<0u, std::tuple<> >’
     class tuple_element;
           ^

【问题讨论】:

  • C++1y,您可以简单地执行(size_t)((void*)&amp;tuple_-(void*)&amp;std::get&lt;T&gt;(tuple)) 之类的操作。否则,您需要对函数进行专门化,以防止展开超出元组的末尾。
  • 这对我来说闻起来像一个 g++ 错误,基于 "error: invalid use of incomplete type ‘struct std::tuple_element&lt;0u, std::tuple&lt;&gt; &gt;’" 的快速 google。
  • @cdhowie 你希望 tuple_size 后面的 tuple_element 是一个完整的类型吗?空的 tuple&lt;&gt; 是由于 tuple_element 的递归性质造成的
  • @PiotrS。我猜想我在看这个的时候有交叉线,我把一件事弄混了——我知道std::tuple_element&lt;0, std::tuple&lt;&gt;&gt;不是一个完整的类型,但出于某种原因正在寻找不同的类型。现在看递归模板还为时过早,我猜......

标签: c++ loops tuples sfinae


【解决方案1】:

您的代码是正确的,除了一个问题。

问题在于您使用std::tie() 辅助函数构造了一个std::tuple,这会产生一个引用元组 - std::tuple&lt;A&amp;, B&amp;, C&amp;&gt;

另一方面,您有offset(const T&amp; t),其中推导类型T 可以只是ABC(也就是说,引用不是推导类型的一部分,仅参数的类型)。也就是说,您的条件 is_same&lt;tuple_element&lt;...&gt;::type, T&gt; 总是失败,因为一种类型是引用,而另一种不是,例如std::is_same&lt;A&amp;, A&gt;。为了解决这个问题,您应该先衰减类型(或至少删除一个引用),然后再将它与另一个类型进行比较以判断是否相等。

话虽如此,你的每一个条件应该如下所示:

std::is_same<typename std::decay<typename std::tuple_element<INDEX, Tuple>::type>::type, T>::value
//           ~~~~~~~~~~~~~~~~~~~^                                               ~~~~~~^

:

std::is_same<std::decay_t<typename std::tuple_element<INDEX, Tuple>::type>::type>, T>::value
//           ~~~~~~~~~~~~^                                                      ^

DEMO

【讨论】:

    【解决方案2】:

    你似乎想要这样的东西:

    template <typename T, typename Tuple> struct get_index;
    
    template <typename T, typename... Ts>
    struct get_index<T, std::tuple<T, Ts...>> : std::integral_constant<std::size_t, 0> {};
    
    template <typename T, typename Tail, typename... Ts>
    struct get_index<T, std::tuple<Tail, Ts...>> :
        std::integral_constant<std::size_t, 1 + get_index<T, std::tuple<Ts...>>::value> {};
    

    然后将所有偏移方法替换为

    template <typename T>
    std::size_t
    offset(const T&)
    {
        return array_[get_index<T&, Tuple>::value];
    }
    

    Live example.

    请注意,在您的示例中 Tuplestd::tuple&lt;A&amp;, B&amp;, C&amp;&gt; 并且您测试与 A 是否相等...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-12
      • 1970-01-01
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 2019-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多