C++ std::get<variable> fails

北城以北 提交于 2019-12-01 03:13:47

问题


How do I use a variable to index into a tuple using std::get<>? I have the following code:

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

and it fails with the following compiler error:

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

I actually truncated the compiler error message as I think it does not add much beyond thid point. Any idea how to make that work?

Edit:

Just to clarify, using an array type is not really an option. I have to use the tuple cause it is the return type of an API from a third party library. The example above is just to make it easy to understand.


回答1:


How do I use a variable to index into a tuple using std::get<>?

You do not, std::get<> parameter value must be known at compile time.

Any idea how to make that work?

yes, use proper type:

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



回答2:


Any idea how to make that work?

Option 1

Use compile time constants to access the std::tuple.

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

Option 2

Use a container type whose elements can be accessed using an index at run time.

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

or

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



回答3:


The likely answer that you should adopt is to just use an array, vector, or other kind of indexed container.


In the event that the tuple elements are not homogeneous types and you actually do need an answer for that case, it's a bit complex. This is because types need to be known at compile time. So where you think you could do std::cout << get_from_tuple(a_tuple, index) for example, this can't work as easily as you think it can because the operator<< overload for sending the object to the standard output stream is selected at compile time. Obviously, this means that the index must be known at compile time, too -- otherwise we can't know the type of the tuple element.

However, it is possible to build a template function that can, in fact, implement exactly this behavior. The end result, when compiled, is a conditional tree that is capable of handling each element in the tuple, but we enlist the compiler to help us build that conditional tree.

What I will build here is a function that, given a tuple, index, and functor, will invoke the functor, forwarding that particular tuple item, and will then return true. If the index is out of range, it will return false.

If the functor is not able to be called with every element in the tuple, the template function will fail to instantiate.

The final solution looks like this:

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



回答4:


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

you can just do:

#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.

There is no unbounded recursion in this solution. It does require C++1z -- you can replace the body of foreacher with the using unused=int[]; trick in C++14.



来源:https://stackoverflow.com/questions/43621098/c-stdgetvariable-fails

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!