Являются ли массивы ковариантными по своим размерам? - PullRequest
3 голосов
/ 19 января 2011

Есть ли способ использовать новый тип std::array полиморфно в размере массива?То есть, если у меня есть функция вида

void DoSomething(std::array<int, 5>& myArray) {
    /* ... */
}

Тогда математически хорошо ли она определена для выполнения следующих действий (даже если это недопустимый код C ++?)

std::array<int, 10> arr;
DoSomething(arr);

Если это математически хорошо определено, есть ли способ написать std::array так, чтобы его элементы массива были смежными, и этот код компилируется?Единственная техника, о которой я мог подумать, - это иметь какую-то странную метапрограмму шаблона, где std::array<T, N+1> наследуется от std::array<T, N>, но я не верю, что это заставляет элементы массива быть смежными.

Ответы [ 3 ]

5 голосов
/ 19 января 2011

Непосредственно? №

Однако вы можете использовать полиморфизм во время компиляции, чтобы достичь чего-то очень похожего, и вы можете написать справочную оболочку, с которой будет легче работать в коде:

#include <array>
#include <cstddef>

template <typename T, std::size_t N>
struct ref_array_of_at_least
{
    template <std::size_t M>
    ref_array_of_at_least(T (&a)[M])
        : data_(a)
    {
        static_assert(M >= N, "Invalid size");
    }

    template <std::size_t M>
    ref_array_of_at_least(std::array<T, M>& a)
        : data_(&a[0])
    {
        static_assert(M >= N, "Invalid size");
    }

    T* data_;
};

Используется как:

void f(ref_array_of_at_least<int, 5>) { }

int main()
{
    std::array<int, 5> x;
    std::array<int, 6> y;
    std::array<int, 4> z;
    f(x); // ok
    f(y); // ok
    f(z); // fail
}

(Вам нужно добавить несколько operator[] перегрузок и тому подобное к ref_array_of_at_least, и для того, чтобы сделать его правильным, требуется некоторая работа, но это начало, которое демонстрирует возможность того, что вы ищете.)

2 голосов
/ 19 января 2011

Если это было требованием, одним из подходов является оператор преобразования в требуемый тип:

#include <iostream>

template <typename T, int N>
struct Array
{
    Array() { for (int i = 0; i < N; ++i) x[i] = 0; }

    template <int N2>
    operator Array<T, N2>&()
    {
        // for safety, static assert that N2 < N...
        return reinterpret_cast<Array<T, N2>&>(*this);
    }

    int size() const { return N; }
    T x[N];

    friend std::ostream& operator<<(std::ostream& os, const Array& a)
    {
        os << "[ ";
        for (int i = 0; i < N; ++i) os << a.x[i] << ' ';
        return os << ']';
    }
};


void f(Array<int, 5>& a)
{
    a.x[a.size() - 1] = -1;
}

int main()
{
    Array<int, 10> a;
    std::cout << a << '\n';
    f(a);
    std::cout << a << '\n';
}

Я бы не советовал: довольно ужасно. Более явный механизм кажется гораздо менее склонным к неправильному использованию, а также более мощным, что-то вроде:

template <size_t N2>
Array<T,N2>& slice(size_t first_index)
{
    return *(Array<T,N2>*)(data() + first_index);
}

// usage...
f(a.slice<5>(3));  // elements 3,4,5,6,7.

(очистить кастинг для дополнительных очков: - /)

0 голосов
/ 19 января 2011

Нет, но вы можете подделать его:

// Hide this function however you like: "detail" namespace, use "_detail"
// in the name, etc.; since it's not part of the public interface.
void f_detail(int size, int *data) {
  use(data, /* up to */ data + size);
}

int const f_min_len = 5;

template<int N>
void f(int (&data)[N]) {
  static_assert(N >= f_min_len);
  f_detail(N, data);
}

template<int N>
void f(std::array<int, N> &data) {
  static_assert(N >= f_min_len);
  f_detail(N, &data[0]);
}

Это полный пример, и он должен работать точно так, как представлено. Вам нужно только изменить тип данных с int (или сделать его параметром шаблона) и добавить const по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...