C ++ std :: get <variable>失败

时间:2017-04-25 21:15:41

标签: c++ stdtuple

如何使用 std :: get&lt;&gt; 使用变量索引元组?我有以下代码:

#include <iostream>
#include <tuple>
using namespace std;

int main() {
  tuple<int, int> data(5, 10);
  for (int i=0; i<2; i++) {
    cout << "#" << i+1 << ":" << get<i>(data) << endl;
  }
  return 0;
}

并且因以下编译器错误而失败:

prog.cpp: In function 'int main()':
prog.cpp:10:39: error: the value of 'i' is not usable in a constant expression
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                       ^
prog.cpp:9:11: note: 'int i' is not const
  for (int i=0; i<2; i++) {
           ^
prog.cpp:10:46: error: no matching function for call to 
    'get(std::tuple<int, int>&)'
          cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                          ^
prog.cpp:10:46: note: candidates are:
In file included from /usr/include/c++/4.9/tuple:38:0,
                 from prog.cpp:2:
/usr/include/c++/4.9/utility:143:5: note: template<unsigned int _Int, 
class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, 
std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
     get(std::pair<_Tp1, _Tp2>& __in) noexcept
     ^
/usr/include/c++/4.9/utility:143:5: note:   template argument 
deduction/substitution failed:
prog.cpp:10:46: error: the value of 'i' is not usable in a constant 
expression
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                              ^
prog.cpp:9:11: note: 'int i' is not const
  for (int i=0; i<2; i++) {
           ^
prog.cpp:10:46: note: in template argument for type 'unsigned int' 
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                              ^
In file included from /usr/include/c++/4.9/tuple:38:0,
                 from prog.cpp:2:
/usr/include/c++/4.9/utility:148:5: note: template<unsigned int _Int, 
class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, 
std::pair<_Tp1, _Tp2> >::type&& std::get(std::pair<_Tp1, _Tp2>&&)
     get(std::pair<_Tp1, _Tp2>&& __in) noexcept
     ^
/usr/include/c++/4.9/utility:148:5: note:   template argument 
deduction/substitution failed:
prog.cpp:10:46: error: the value of 'i' is not usable in a constant 
expression
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                              ^
prog.cpp:9:11: note: 'int i' is not const
  for (int i=0; i<2; i++) {
           ^
prog.cpp:10:46: note: in template argument for type 'unsigned int' 
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                              ^
In file included from /usr/include/c++/4.9/tuple:38:0,
                 from prog.cpp:2:
/usr/include/c++/4.9/utility:153:5: note: template<unsigned int _Int, 
class _Tp1, class _Tp2> constexpr const typename 
std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const 
std::pair<_Tp1, _Tp2>&)
     get(const std::pair<_Tp1, _Tp2>& __in) noexcept
     ^
/usr/include/c++/4.9/utility:153:5: note:   template argument 
deduction/substitution failed:
prog.cpp:10:46: error: the value of 'i' is not usable in a constant 
expression
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                              ^
prog.cpp:9:11: note: 'int i' is not const
  for (int i=0; i<2; i++) {
           ^
prog.cpp:10:46: note: in template argument for type 'unsigned int' 
      cout << "#" << i+1 << ":" << get<i>(data) << endl;
                                              ^
In file included from /usr/include/c++/4.9/tuple:38:0,
                 from prog.cpp:2:
/usr/include/c++/4.9/utility:162:5: note: template<class _Tp, class 
_Up> constexpr _Tp& std::get(std::pair<_T1, _T2>&)
     get(pair<_Tp, _Up>& __p) noexcept

我实际上截断了编译器错误消息,因为我认为它并没有超出这一点。知道如何做到这一点吗?

编辑:

只是澄清一下,使用array类型并不是一个真正的选择。我必须使用tuple因为它是来自第三方库的API的返回类型。上面的例子只是为了让它易于理解。

4 个答案:

答案 0 :(得分:5)

  

如何使用变量使用std :: get&lt;&gt;索引到元组?

你没有,std::get<>参数值必须在编译时知道。

  

知道如何做到这一点吗?

是的,请使用正确的类型:

int main() {
  std::array<int, 2> data{ 5, 10 };
  for (int i=0; i<2; i++) {
    cout << "#" << i+1 << ":" << data[i] << endl;
  }
  return 0;
}

答案 1 :(得分:3)

  

知道如何做到这一点吗?

选项1

使用编译时常量来访问std::tuple

cout << "#" << 1 << ":" << get<0>(data) << endl;
cout << "#" << 2 << ":" << get<1>(data) << endl;

选项2

使用容器类型,其元素可以在运行时使用索引进行访问。

std::vector<int> data{5, 10};

std::array<int, 2> data{5, 10};

答案 2 :(得分:2)

您应该采用的可能答案是使用数组,向量或其他类型的索引容器。

如果元组元素不是同类型的,并且您确实需要对该案例进行答案,那么它有点复杂。这是因为需要在编译时知道类型。所以你认为你可以做std::cout << get_from_tuple(a_tuple, index),你可以像你想象的那样轻松工作,因为在编译时选择了将对象发送到标准输出流的operator<<重载时间。显然,这意味着索引必须在编译时知道 - 否则我们无法知道元组元素的类型。

但是,可以构建一个模板函数,实际上可以实现这种行为。编译时,最终结果是一个条件树,它能够处理元组中的每个元素,但我们请求编译器帮助我们构建该条件树。

我将在这里构建的是一个函数,给定一个元组,索引和仿函数,它将调用该仿函数,转发该特定元组项,然后返回true。如果索引超出范围,则返回false。

如果无法使用元组中的每个元素调用仿函数,则模板函数将无法实例化。

final solution看起来像这样:

#include <tuple>
#include <type_traits>

namespace detail {
    template <std::size_t I>
    struct size_wrapper { };

    template <typename V, typename Tup, std::size_t I>
    bool visit_tuple_index_impl(Tup && t, std::size_t index, V && visitor, size_wrapper<I>)
    {
        if (index == I - 1) {
            visitor(std::get<I - 1>(std::forward<Tup>(t)));
            return true;
        }

        return visit_tuple_index_impl(std::forward<Tup>(t), index, visitor, size_wrapper<I - 1>());
    }

    template <typename V, typename Tup>
    bool visit_tuple_index_impl(Tup &&, std::size_t, V &&, size_wrapper<0>)
    {
        return false;
    }
}

template <typename V, typename Tup>
bool visit_tuple_index(Tup && t, std::size_t index, V && visitor)
{
    return detail::visit_tuple_index_impl(
        std::forward<Tup>(t),
        index,
        std::forward<V>(visitor),
        detail::size_wrapper<std::tuple_size<typename std::decay<Tup>::type>::value>()
    );
}

答案 3 :(得分:0)

#include <utility>

template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&& f)->decltype(auto){
    return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
  };
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
  return index_over( std::make_index_sequence<N>{} );
}
template<class F>
auto foreacher( F&& f ) {
  return [f=std::forward<F>(f)](auto&&...args)mutable {
    (void(), ..., void(f(decltype(args)(args))));
  };
}
template<std::size_t N>
auto count_upto( std::integral_constant<std::size_t, N> ={} ) {
  return [](auto&& f){
    index_upto<N>()(foreacher(decltype(f)(f)));
  };
}

你可以这样做:

#include <iostream>
#include <tuple>

int main() {
  std::tuple<int, int> data(5, 10);
  count_upto<2>()([&](auto I){
    std::cout << "#" << (I+1) << ":" << std::get<I>(data) << "\n";
  });
}

Live example

此解决方案中没有无限递归。它确实需要C ++ 1z - 您可以使用C ++ 14中的foreacher技巧替换using unused=int[];的正文。