简体   繁体   中英

Passing unknown size std::Array in a function with template. How can I correct this code?

I wrote this code using 2d Vector and Array. But I wanted to use std::array this time and my code did not work because this was the first time I use std::array and template.

It gave me for this line:

array<array<int, sizeY>, sizeX> arr;

this error:

Error C2971 std::array : template parameter _Size : sizeY,sizeX : a variable with non-static storage duration cannot be used as a non-type argument

#include <iostream>
#include <array>
using namespace std;

template <size_t Y, size_t X>
bool IsMagicSquare(array<array<int, Y>, X>& ar)
{
    int x = ar.size();
    int y = ar[0].size();

    if (x == y)
    {
        int ver[x] = {  };
        int hor[y] = {  };
        int cross0 = 0;
        int cross1 = 0;

        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                hor[i] += ar[i][j];
                ver[j] += ar[i][j];
                if (i == j)
                    cross0 += ar[i][j];
                if (i + j == x - 1)
                    cross1 += ar[i][j];
            }
        }
        if (cross0 != cross1)
            return false;
        else
        {
            for (int i = 0; i < x; i++)
                if ((cross0 != ver[i]) || (cross1 != hor[i]))
                    return false;
        }
    }
    else
        return false;

    return true;
}
int main()
{
    int sizeX, sizeY;
    cout << "Size of Matrix:";
    cin >> sizeX >> sizeY;

  **array<array<int, sizeY>, sizeX> arr;**

    cout << "Elements of the Matrix:";
    for (int i = 0; i < sizeX; i++)
        for (int j = 0; j < sizeY; j++)
            cin >> arr[i][j];

    if (IsMagicSquare(arr))
    {
        for (int i = 0; i < sizeX; i++)
        {
            cout << "\n";
            for (int j = 0; j < sizeY; j++)
                cout << arr[i][j];
        }
    }
    else
        cout << "Matrix is not magical square!";

    return 0;
}

The size of an array (or template arguments in general) has to be known at compile-time, so there is no way to use the runtime values sizeX , sizeY as size (template argument) for an array.

You have to use a variable-length container like std::vector instead.

For reference, here's how you can get a std::array with a size which is decided at runtime:

#include <array>
#include <cstddef>
#include <iostream>
#include <memory>

template<typename T>
struct DynArray {
    virtual std::size_t size() const = 0;
    virtual T * data() = 0;
    virtual ~DynArray() {}
};
template<typename T, std::size_t Size>
struct DynArrayImpl : public DynArray<T> {
    std::array<T, Size> array;
    std::size_t size() const override {
        return array.size();
    }
    T * data() override {
        return array.data();
    }
};

template<typename T, std::size_t Size>
struct DynArrayFactory {
    static DynArray<T> * allocate(std::size_t const size) {
        if (size > Size) {
            // ERROR
            return nullptr;
        }
        if (size == Size) {
            return new DynArrayImpl<T, Size>();
        }
        return DynArrayFactory<T, Size - 1>::allocate(size);
    }
};

template<typename T>
struct DynArrayFactory<T, 0> {
    static DynArray<T> * allocate(std::size_t const size) {
        if (size > 0) {
            return nullptr;
        }
        return new DynArrayImpl<T, 0>();
    }
};


int main() {
    std::size_t size;
    std::cin >> size;
    std::unique_ptr<DynArray<int>> array{DynArrayFactory<int, 100>::allocate(size)};
    std::cout << array->size() << std::endl;
}

This requires a maximum size (100 in this case) to be specified at compile time and is a really convoluted way of doing things; thus not recommended .

Accessing the std::array is nearly impossible though, unless with similar templated code which then generates code for each possible size (see below). This will generate a lot of code. One can easily access the contents of the array however, as seen in the example above. But really: use std::vector .


"similar templated code":

template<std::size_t Size>
struct FillWithNumbers {
    static void run(std::array<int, Size> & array) {
        int n = 0;
        std::generate(begin(array), end(array), [&n](){ return n++; });
    }
};

template<typename T, std::size_t Size>
struct DynArrayApply {
    template<template<std::size_t S> class Fn>
    static void apply(DynArray<T> & array) {
        if (array.size() > Size) {
            // ERROR
        }
        if (array.size() == Size) {
            DynArrayImpl<T, Size> & real = dynamic_cast<DynArrayImpl<T, Size> &>(array);
            Fn<Size>::run(real.array);
        }
        else {
            DynArrayApply<T, Size - 1>::template apply<Fn>(array);
        }
    }
};
template<typename T>
struct DynArrayApply<T,0> {
    template<template<std::size_t S> class Fn>
    static void apply(DynArray<T> & array) {
        if (array.size() > 0) {
            // ERROR
        }
        DynArrayImpl<T, 0> & real = dynamic_cast<DynArrayImpl<T, 0> &>(array);
        Fn<0>::run(real.array);
    }
};


int main() {
    std::size_t size;
    std::cin >> size;
    std::unique_ptr<DynArray<int>> array{DynArrayFactory<int, 100>::allocate(size)};
    DynArrayApply<int, 100>::apply<FillWithNumbers>(*array);
    std::cout << array->size() << std::endl;
    std::cout << array->data()[array->size() / 2] << std::endl;
}

I wrote this code using 2d Vector and Array.

That is appropriate, as you do not know the size of the matrix until run time.

But I wanted to use std::array this time [...]

Well, that's a problem because the size of a std::array must be known at compile time. Moving away from C-style arrays is a recommended move, but you have to know where to go. Use the correct tool for the job at hand.

Fixed-size arrays: For arrays whose size is known by the compiler, a std::array is a reasonable replacement. In fact, the std::array is probably nothing more than the C-style array with a different interface.

Variable-size arrays: For arrays whose size is not known until run time, a std::vector is a reasonable replacement. Even though the name does not say "array", it is an array. It is a bit more complex than std::array , but that is because it supports sizes not known at compile time.

This distinction tends to be better-known by those not using gcc, as that compiler has an extension that supports declaring variable-size C-style arrays using the same syntax as declaring fixed-size C-style arrays. It is standard C++ to declare an array along the lines of int col[10] . However, it is not standard C++ to declare an array along the lines of int col[sizeY] , where sizeY has a value supplied at run time. The latter syntax is supported by gcc as an extension, and some people use it without realizing it is an extension (ported from gcc's C support). To some extent, std::vector makes this extension available in a more portable form.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM