开发者

Are arrays covariant in their size?

Is there a way to use the new std::array type polymorphically in the size of the array? That is, if I have a function of the form

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

Then is it mathematically well-defined to do the following (even if it's not legal C++ code?)

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

Imof this is mathematically well-defined, is there a way to write std::array such that its array elements are contiguous and this code compiles? The only technique I could think of would be to have some weird template metaprogram 开发者_StackOverflow社区where std::array<T, N+1> inherits from std::array<T, N>, but I don't believe that forces the array elements to be contiguous.


Directly? No.

You can, however, use compile-time polymorphism to achieve something very similar, and you can write a reference wrapper that makes it easier to work with in the code:

#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_;
};

Used as:

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
}

(You'd need to add some operator[] overloads and such to ref_array_of_at_least, and it needs some work to make it const correct, but it's a start that demonstrates the possibility of what you are seeking.)


If this was a requirement, one approach is a conversion operator to the required type:

#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';
}

I wouldn't recommend it though: pretty horrid. A more explicit mechanism seems a lot less prone to misuse, as well as being more powerful - something vaguely like:

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.

(clean up the casting for extra points :-/)


No, but you can fake it:

// 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]);
}

This is a complete example, and should work exactly as presented. You'd only have to change the data type from int (or make it a template parameter) and add const as required.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜