How to get dereferenced type of template member for function return type

How can I get the correct type T for the next to_vector function?

 template<typename K> struct A { K* p; size_t n; std::string foo; }; template<typename K> struct B { K* p; size_t n; float bar[3]; }; template<typename X> std::vector<T> to_vector(const X& x) { // what is T? return std::vector<T>(xp, x.p+xn); } 

I tried with decltype(*std::declval<X>().p) , but this leads to error: forming pointer to reference type 'float&' for the following example:

 A<float> a = { new float[10], 10, "hello" }; std::vector<float> v = to_vector(a); 

This is part of a larger code, and there are more types like A and B But everyone has a pointer p and a length n .

+6
source share
5 answers

Perhaps you can use

 typename std::decay<decltype(*X::p)>::type 

for T , because decltype is an invaluable context, and therefore X::p is legal here. Furthermore, std::decay seems appropriate as it combines std::remove_reference with std::remove_cv .

+5
source

You're on the right track, you just need to use the appropriate utility to get rid of the pointer / link.

There is std::remove_reference , which you can use as:

 typename std::remove_reference<decltype(*std::declval<X>().p)>::type 

but it is a bit simpler than std::remove_pointer instead:

 typename std::remove_pointer<decltype(std::declval<X>().p)>::type 

(see, * left, otherwise the same).

You might want to move std :: remove_cv to the mix if the pointer can be cv-qualified, since vector elements should not be.


As indicated in another now deleted answer, you can write xp instead of std::declval<X>().p if you use a declaration of the return type of the return type:

 template <typename X> auto to_vector(const X& x) -> std::vector<typename std::remove_pointer<decltype(xp)>::type> 
+4
source

This answer is pretty much out of date.

 template<typename X, typename T = typename std::remove_reference<decltype(*X().p)>::type> std::vector<T> to_vector(const X& x) { return std::vector<T> (xp, x.p+xn); } 
+4
source

You can use the features:

 template<typename K> struct A { K* p; size_t n; std::string foo; typedef K my_type; }; template<typename K> struct B { K* p; size_t n; float bar[3]; typedef K my_type; }; template<typename X> std::vector<typename X::my_type> to_vector(const X& x) { return std::vector<typename X::my_type>(xp, x.p+xn); } A<float> a = { new float[10], 10, "hello" }; std::vector<float> v = to_vector(a); 
0
source

So, here is the answer entirely from the left margin.

You must rotate your types A and B into iterable objects, overloading begin and end in their namespace.

There are several approaches for this:

1) You state that each of them implements member- begin and member- end , which returns a pointer as an iterator. Alternatively, the free begin and end function does the same.

2) You need them to inherit from the helper class CRTP, which does this: it either implements begin and end for you, or allows overloading the begin and end function, which can with ADL.

3) If all such classes are in some namespace that you control, and you want to process the fields K* p and std::size_t n as evidence that it should be considered as an iterable range, then we can do this with "global " begin and end , which SFINAE uses to apply only in this case.

I would recommend # 1 or # 2.

For # 2:

 template<typename Derived> struct p_n_iterable { Derived* self() { static_assert( std::is_base_of<p_n_iterable, Derived>::value, "CRTP failure" ); return static_cast<Derived*>(this); } Derived const* self() const { static_assert( std::is_base_of<p_n_iterable, Derived>::value, "CRTP failure" ); return static_cast<Derived const*>(this); } typedef typename std::decay< decltype( *Derived::p ) >::type value_type; typedef value_type* iterator; std::size_t size() const { return self()->n; } iterator begin() { return self->p; } iterator end() { return begin() + size(); } }; 

if I wrote this right, change A to:

 template<typename K> struct A : p_n_iterable<A<K>> { ... unchanged ... }; 

and suddenly loops like for( auto x:a ) work on A

I think the cost of adding this little preamble to classes is worth using.

To do this with # 3, we create a traits class that determines whether it should be n_p_iterable , checking that T::n is of type std::size_t and T::p is a pointer type. I would advise against this, because although it requires fewer templates elsewhere, it is pretty hacky.

Once we get this, we can write a really generic to_vector .

First, write yourself get_iterator_type<Container> :

 namespace adl_helper { using std::begin; using std::end; template<typename C> auto adl_begin(C&& c)->decltype(begin( std::forward<C>(c) )); template<typename C> auto adl_end(C&& c)->decltype(end( std::forward<C>(c) )); } using adl_helper::adl_begin; using adl_helper::adl_end; template<typename... Ts> struct type_sink { typedef void type; } template<typename... Ts> using TypeSink = typename type_sink<Ts...>::type; template<typename Container, typename=void> struct get_iterator_type {}; template<typename Container> struct get_iterator_type< Container, TypeSink< adl_begin( std::declval<Container&>() ) > > { typedef adl_begin( std::declval<Container&>() ) type; }; template<typename Container, typename=void> struct get_value_type {}; template<typename Container> struct get_value_type< Container, TypeSink< std::iterator_traits< typename get_iterator_type<Container>::type > > > { typedef std::iterator_traits< typename get_iterator_type<Container>::type > > traits; typedef typename traits::value_type type; }; 

write our to_vector :

 template<typename C> auto to_vector( C&& container )-> std::vector<typename get_value_type<typename remove_reference<C>::type>::type> { std::vector<typename get_value_type<typename remove_reference<C>::type>::type> retval; for( auto&& x : std::forward<C>(container) ) { retval.push_back(x); } return retval; } 

and if I dotted all i and crossed all t , you now have both a complete iteration of C ++ 11 style for your types, and to_vector , which works on both your types and other containers, such as a std::map ).

Further improvements may include determining whether the container passing through it has size or has random access iterators, and if so, reserve the size in retval . But this post is long enough.

0
source

All Articles