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?
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]:
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any)
and in this function:
template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes)
-> std::vector<decltype(make_vector<T>(sizes...))> {
...
}
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;
template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes)
{ /* exactly as before */ }
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:
template <typename T, size_t N>
struct MakeVector
{
template <typename... Args>
static auto make_vector(std::size_t first, Args... sizes)
-> std::vector<decltype(MakeVector<T, N-1>::make_vector(sizes...))>
{
auto inner = MakeVector<T, N-1>::make_vector(sizes...);
return std::vector<decltype(inner)>(first, inner);
}
};
template <typename T>
struct MakeVector<T, 1>
{
static std::vector<T> make_vector(std::size_t size) {
return std::vector<T>(size);
}
};
template <typename T, typename... Args>
auto make_vector(Args... args)
-> decltype(MakeVector<T, sizeof...(Args)>::make_vector(args...))
{
return MakeVector<T, sizeof...(Args)>::make_vector(args...);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With