简体   繁体   中英

How to correctly propagate the type of a universal reference?

I have a situation in my codebase where I must implement a generic form of std::get() which works for any kind of Tuple-like type. The function accepts a universal reference to a Tuple and returns a reference to the I th element of the Tuple . I don't know how to name the type of the reference. Unfortunately, I'm unable to use an auto return type and just let the compiler figure it out.

Here's my first attempt:

#include <type_traits>
#include <tuple>

template<class T, class U>
struct propagate_reference
{
  using type = U;
};

template<class T, class U>
struct propagate_reference<T&,U>
{
  using type = typename std::add_lvalue_reference<U>::type;
};

template<class T, class U>
struct propagate_reference<T&&,U>
{
  using type = typename std::add_rvalue_reference<U>::type;
};

template<size_t I, class TupleReference>
struct get_result
{
  using tuple_type = typename std::decay<TupleReference>::type;

  using type = typename propagate_reference<
    TupleReference,
    typename std::tuple_element<I,tuple_type>::type
  >::type;
};

template<size_t I, class Tuple>
typename get_result<I,Tuple&&>::type my_get(Tuple&& t)
{
  return std::get<I>(std::forward<Tuple>(t));
}

int foo(const std::tuple<int>& t)
{
  return my_get<0>(t);
}

int main()
{
  return 0;
}

Clang rejects this program:

$ clang -std=c++11 test_get.cpp 
test_get.cpp:36:10: error: binding of reference to type 'int' to a value of type 'const __tuple_element_t<0UL, tuple<int> >' (aka 'const int') drops qualifiers
  return std::get<I>(std::forward<Tuple>(t));
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_get.cpp:41:10: note: in instantiation of function template specialization 'my_get<0, const std::tuple<int> &>' requested here
  return my_get<0>(t);
         ^
1 error generated.

I suspect the problem is with the way I instantiate get_result . What am I doing wrong?

The problem is that std::decay removes cv-qualifiers, and defines the resulting type as the member typedef type. As already TC mentioned in the comments what you need here is std::remove_reference :

template<size_t I, class TupleReference>
struct get_result
{
  using tuple_type = typename std::remove_reference<TupleReference>::type;

  using type = typename propagate_reference<
    TupleReference,
    typename std::tuple_element<I,tuple_type>::type
  >::type;
};

Live Demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM