I am trying to make a function that returns zero filled array of array of array... Following Elegantly define multi-dimensional array in modern C++ I defined:
template<typename U, std::size_t N, std::size_t... M>
struct myTensor{
using type = std::array<typename myTensor<U, M...>::type, N>;
};
template<typename U, std::size_t N>
struct myTensor<U,N>{
using type = std::array<U, N>;
};
template<typename U, std::size_t... N>
using myTensor_t = typename myTensor<U, N...>::type;
Then I define the following template functions to fill with zeros:
template<typename U, std::size_t N, std::size_t... M>
myTensor_t<U, N, M...> Zero_Tensor(){
myTensor_t<U, N, M...> res;
for(int i=0; i<N; i++)
res[i] = Zero_Tensor<U, M...>();
return res;
};
template<typename U, std::size_t N>
myTensor_t<U, N> Zero_Tensor(){
myTensor_t<U, N> res;
for(int i=0; i<N; i++)
res[i] = U(0);
return res;
};
When I do for example
class myclass{
myTensor_t<int,3,3,5> x;
};
it compiles fine. If I try doing:
class myclass{
myTensor_t<int,3,3,5> x=Zero_Tensor<int,3,3,5>();
};
I get the following error at compile:
src/mytensor.hpp(107): error: no instance of overloaded function "Zero_Tensor" matches the argument list
res[i] = Zero_Tensor<U, M...>();
^
src/mytensor.hpp(112): note: this candidate was rejected because function is not visible
myTensor_t<U, N> Zero_Tensor(){
^
src/mytensor.hpp(104): note: this candidate was rejected because at least one template argument could not be deduced
myTensor_t<U, N, M...> Zero_Tensor(){
^
detected during:
instantiation of "myTensor_t<U, N, M...> Zero_Tensor<U,N,M...>() [with U=int, N=5UL, M=<>]" at line 107
instantiation of "myTensor_t<U, N, M...> Zero_Tensor<U,N,M...>() [with U=int, N=3UL, M=<5UL>]" at line 107
instantiation of "myTensor_t<U, N, M...> Zero_Tensor<U,N,M...>() [with U=int, N=3UL, M=<3UL, 5UL>]" at line 36 of "src/myclass.hpp"
I don't really understand what this candidate was rejected because function is not visible
is telling me. I guess I don't understand why it is not visible? Any help is appreciated.
Suggestion: rewrite your Zero_Tensor()
functions as follows
template <typename U>
U Zero_Tensor ()
{ return U(0); }
template <typename U, std::size_t N, std::size_t... M>
myTensor_t<U, N, M...> Zero_Tensor ()
{
myTensor_t<U, N, M...> res;
for ( auto i = 0u ; i < N ; ++i )
res[i] = Zero_Tensor<U, M...>();
return res;
}
Now your problem is that the ground version (the end of the recursion, the <U, N>
version) is defined after recursive version (the <U, N, M...>
), so when the recursive version call
Zero_Tensor<U, M...>();
and the M...
pack is empty, the compiler doesn't know a Zero_Tensor()
function that accept only a U
type as template parameter.
You can invert the order of definitions (or declare the ground case before the recursive case) but you have another problem: when you call
Zero_Tensor<U, M...>();
and the M...
pack contain only a number, you have an ambiguous call because both version match.
Solution: use a simpler ground case
template <typename U>
U Zero_Tensor ()
{ return U(0); }
and define it before the recursive case.
This way the
Zero_Tensor<U, M...>();
is never ambiguous because when the M...
pack is empty, only the ground case matches, otherwise only the recursive case matches.
Off topic suggestion: when you have a cycle from zero to an unsigned number (as in your example)
// ............V N is std::size_t, an unsigned type
for(int i=0; i<N; i++)
use an unsigned variable for the index variable (i
) to avoid an annoying warning as "warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’"