Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create n-dimensional vector with given sizes

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?

like image 487
RiaD Avatar asked May 01 '15 21:05

RiaD


1 Answers

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...);
}
like image 87
Barry Avatar answered Sep 29 '22 20:09

Barry