2015-12-07 1 views
3

J'ai une situation dans mon code où je dois implémenter une forme générique de std::get() qui fonctionne pour tout type de type Tuple. La fonction accepte une référence universelle à un Tuple et renvoie une référence à l'élément I du Tuple. Je ne sais pas comment nommer le type de la référence. Malheureusement, je ne peux pas utiliser un type de retour auto et laisser le compilateur le découvrir.Comment propager correctement le type d'une référence universelle?

Voilà ma première tentative:

#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 rejette ce programme:

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

Je soupçonne que le problème est avec la façon dont j'instancier get_result. Qu'est-ce que je fais mal?

+2

Pourquoi vous ne parvenez pas à retourner auto? – 101010

+2

C'est ici que vous avez besoin de 'remove_reference' et non de' decay'. –

+0

Qu'est-ce qui ne fonctionne pas exactement avec 'std :: get'? – PiotrNycz

Répondre

3

Le problème est que std::decay supprime les qualificatifs cv et définit le type résultant comme type typedef de membre. Comme il a déjà TC mentionné dans les commentaires ce que vous avez besoin ici est 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