I have the following problem. I define a N dimensional vector as so
#include <vector>
#include <utility>
#include <string>
template <int N, typename T>
struct NVector{
typedef std::vector<typename NVector<N-1,T>::type> type;
};
template <typename T> struct NVector<1,T> {
typedef std::vector<T> type;
};
I wish to write a higher order function Map that can transform the leaf elements of the nested vector no matter how deep and return a new nested vector of the same shape. I have tried
template <int N, typename T, typename Mapper>
struct MapResult {
typedef decltype ( (std::declval<Mapper>()) (std::declval<T>()) ) basic_type;
typedef typename NVector<N, basic_type>::type vector_type;
};
template <int N, typename T, typename Mapper>
typename MapResult<N,T,Mapper>::vector_type
Map( typename NVector<N,T>::type const & vector, Mapper mapper)
{
typename MapResult<N,T,Mapper>::vector_type out;
for(auto i = vector.begin(); i != vector.end(); i++){
out.push_back(Map(*i,mapper));
}
return out;
}
template <typename T, typename Mapper>
typename MapResult<1,T,Mapper>::vector_type
Map(typename NVector<1,T>::type const & vector, Mapper mapper)
{
typename MapResult<1,T,Mapper>::vector_type out;
for(auto i = vector.begin(); i != vector.end(); i++){
out.push_back(mapper(*i));
}
return out;
}
and then use it in main like
int main(){
NVector<1,int>::type a = {1,2,3,4};
NVector<2,int>::type b = {{1,2},{3,4}};
NVector<1,std::string>::type as = Map(a,[](int x){return std::to_string(x);});
NVector<2,std::string>::type bs = Map(b,[](int x){return std::to_string(x);});
}
However I get compile errors
<source>:48:34: error: no matching function for call to 'Map'
NVector<1,double>::type da = Map(a,[](int x)->int{return (double)x;});
^~~
<source>:20:5: note: candidate template ignored: couldn't infer template argument 'N'
Map( typename NVector<N,T>::type const & vector, Mapper mapper)
^
<source>:31:5: note: candidate template ignored: couldn't infer template argument 'T'
Map(typename NVector<1,T>::type const & vector, Mapper mapper)
^
<source>:49:34: error: no matching function for call to 'Map'
NVector<2,double>::type db = Map(b,[](int x)->int{return (double)x;});
^~~
<source>:20:5: note: candidate template ignored: couldn't infer template argument 'N'
Map( typename NVector<N,T>::type const & vector, Mapper mapper)
^
<source>:31:5: note: candidate template ignored: couldn't infer template argument 'T'
Map(typename NVector<1,T>::type const & vector, Mapper mapper)
^
2 errors generated.
Compiler returned: 1
I'm guessing that the compiler isn't smart enough ( or the standard doesn't specify how ) to figure out the parameter N by deduction. Is there a way I can achieve this?
I previously had this working but in a different way by actually deriving from std::vector but I don't like this solution as it would be nice to have it work with currently existing code without having to introduce a new wrapper type.
/// define recursive case
template <int N, typename T>
struct NVector : std::vector<NVector<N-1,T>>;
/// define termination case
template <typename T>
struct NVector<1, T> : public std::vector<T>;
live code at https://godbolt.org/z/AMxpuj
T
as argument and useenable_if
– Scintilla