So, what I want is to create multidimensional vector of given type where the first dimension will have size of the first argument of a function call, etc, for example if I do
std::size_t n = 5;
auto x = make_vector<int>(n + 1, n * 2, n * 3);
x
should be 6x10x15 3d array (consisting of zeroes, because I want to default construct right now)
I have tried this:
template <typename T>
std::vector<T> make_vector(std::size_t size) {
return std::vector<T>(size);
}
template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes) -> std::vector<decltype(make_vector<T>(sizes...))> {
auto inner = make_vector<T>(sizes...);
return std::vector<decltype(inner)>(first, inner);
}
It seems to work for 1 or 2 arguments, but fails for 3 arguments with following error(clang++)
In file included from /Users/riad/ClionProjects/for-jhelper/output/run.cpp:1:
/Users/riad/ClionProjects/for-jhelper/tasks/TaskC.cpp:12:12: error: no matching function for call to 'make_vector'
auto x = make_vector<int>(n + 1, n * 2, n * 3);
^~~~~~~~~~~~~~~~
/Users/riad/ClionProjects/for-jhelper/tasks/../spcppl/make_vector.hpp:9:6: note: candidate template ignored: substitution failure [with T = int, Args = <unsigned long, unsigned long>]: call to function 'make_vector' that is neither visible in the template definition nor found by argument-dependent lookup
auto make_vector(std::size_t first, Args... sizes) -> std::vector<decltype(make_vector<T>(sizes...))> {
^ ~~~~~~~~~~~
/Users/riad/ClionProjects/for-jhelper/tasks/../spcppl/make_vector.hpp:4:16: note: candidate function template not viable: requires single argument 'size', but 3 arguments were provided
std::vector<T> make_vector(std::size_t size) {
If I understand correctly problem is that when compiler tries to calculate return value of make_vector it have to know the return value of vector with less number of arguments and fails to do so. How do I fix that?
I manage to do this by calculating the type separately but that seems unnecessary hard despite it's quite short.
Will really appreciate more elegant solution
I was unaware about the other answers when I posted this. Not deleting it in case it could be useful to someone. Ofcourse the solution is quite trivial with C++14 enabled.
With [below code](Demo@ideone you can achieve:
Here is the code with minimal comments:
The easiest solution to this is to drop back to C:
As I said, this is not possible in C++: the C++ standard requires all array sizes to be compile time constants. C is much more flexible in this regard, allowing run time array sizes everywhere since C99, even within
typedef
s.So, when I have some code that has to do serious work with multidimensional arrays, I seriously ask myself whether it is worth to move this code into a pure .c file and just link it together with the rest of my project.
Create a namespace to put some helpers in it, called
details
.In
details
create a typestruct adl_helper{};
Create an implementation of
make_vector
, except its first parameter is always a template parameter calledAdl
. This template parameter is never named, and instances of it are passed to recursions.Implement
make_vector( blah )
by callingdetails::make_vector<T>( details::adl_helper{}, blah )
.What is going on here is that a seemingly recursive call in the signature of a template function is evaluated in two contexts.
First, it is evaluated where it is declared. In particular, it is evaluated before itself has been defined. So it doesn't "catch" itself.
Second, an ADL (argument dependent lookup) based pass is done at the point where it is instantiated based only off template types passed to the function.
The
template<class Adl>
andadl_helper
types means that this argument dependent lookup can see thedetails::make_vector
function itself when it is instantiated. And when it looks up the return type, it can also seedetails::make_vector
. Etc.live example.
The use of
class R=
aliases and the like is just there to cleanup the code and reduce some needless duplication.In this particular case, the effort required to build the return type is easier than all this gymnastics.
I think this solution is cleaner:
Interesting question! The problem you're running into is that unqualified name lookup will look in the scopes of where it is used (in increasing order of generality). But, from [basic.scope.pdecl]:
and in this function:
The "complete declarator" includes the trailing-return-type. So the function template
make_vector<T, Args...>
will not be in scope yet until the{
. That's why this won't compile for 3+ arguments.The simplest fix would be if you had access to C++14, you simply wouldn't need the trailing return type;
Within the body of the name function, you can make the recursive call with no issues.
Without C++14, you could forward it to a class template whose name will be in the scope of all the recursive calls: