Cannot Pass A Section Of An std::array As A Template Reference Type Argument

204 Views Asked by At

I would like to keep arrays of similar data contiguous for processing. For simplification let's say that I would like to keep an array of ints separate from an array of chars so that when I want to display the characters I can loop through all of the characters contiguously without having to fetch the integers. Now the first 32 indices of both arrays belong to one object, and the following 32 to another object. I would like to be able to apply a sort of "blueprint" or "cookie cutter" to the arrays to group each objects "owned" sections of the arrays together. My first thought was to give each object a member variable linked to its section of the array like so

struct NonTemp {
    std::array<int, 32>& ints;
    std::array<char, 32>& chars;

    NonTemp(std::array<int, 32>& Ints, std::array<char, 32>& Chars) : ints(Ints), chars(Chars) {}
};

But I found out that the size of such an object is 8 bytes on a 32 bit build, 16 on a 64 bit build. It was storing the pointers. The addresses are known at compile time so I figured I should be able to replace the pointers with hard coded addresses. Templates are great for making the compiler figure that kind of thing out, so then I tried this:

template<std::array<int, 32>& ints, std::array<char, 32>& chars>
struct Temp {

    std::array<int, 32>& GetInts() {
        return ints;
    }
    
    std::array<char, 32>& GetChars() {
        return chars;
    }
};

std::array<int, 64> ints;
std::array<char, 62> chars;

void main() {
    static std::array<int, 32>& proxyInts = *reinterpret_cast<std::array<int, 32>*>(&ints[32]);
    static std::array<char, 32>& proxyChars = *reinterpret_cast<std::array<char, 32>*>(&chars[32]);

    Temp<proxyInts, proxyChars> temp;
}

But I get a compiler error stating that a compile-time constant expression was expected. Doing some research I found that another answer on stackoverflow may explain why: Reference as a non-type template argument. It claims that a reference variable is not an id-expression (similar to string literals) and cannot be used as a template argument. So is there a work around for this? Or is there a better way in general to accomplish what I am trying to do? For ease of testing I have provided some complete code for the situation:

/*
* Stores pointers to references, increases object size by pointer size each
*/
struct NonTemp {
    std::array<int, 32>& ints;
    std::array<char, 32>& chars;

    NonTemp(std::array<int, 32>& Ints, std::array<char, 32>& Chars) : ints(Ints), chars(Chars) {}
};

template<std::array<int, 32>& ints, std::array<char, 32>& chars>
struct Temp {

    std::array<int, 32>& GetInts() {
        return ints;
    }
    
    std::array<char, 32>& GetChars() {
        return chars;
    }
};

/*
* Replaces references to in with hard coded addresses, does not add to object size
*/
template<int& in>
struct Attempt {
    int& GetInt() {
        return in;
    }
};


std::array<int, 64> ints;
std::array<char, 62> chars;
int in;

void main() {
    NonTemp nonTemp = NonTemp(*reinterpret_cast<std::array<int, 32>*>(&ints), *reinterpret_cast<std::array<char, 32>*>(&chars));
    std::cout << "Size of NonTemp: " << sizeof(nonTemp) << std::endl;


    static std::array<int, 32>& proxyInts = *reinterpret_cast<std::array<int, 32>*>(&ints[32]);
    static std::array<char, 32>& proxyChars = *reinterpret_cast<std::array<char, 32>*>(&chars[32]);

    Temp<proxyInts, proxyChars> temp;
    std::cout << "Size of Temp: " << sizeof(temp) << std::endl;
    temp.GetInts()[0] = 2;

    Attempt<in> attempt;

    std::cout << "Size of Attempt: " << sizeof(attempt) << std::endl;

    attempt.GetInt() = 10;

    std::cout << "In: " << in << std::endl;

    std::cout << "GetInt(): " << attempt.GetInt() << std::endl;

    for (uint32_t i = 0; i < 64; ++i) {
        std::cout << "Arr[" << i << "]: " << ints[i] << std::endl;
    }
}

As an additional note, it might not always be the case that each object "owns" the same number of elements from the arrays, I would like to have a way for different objects with similar data to own varying sized sections of the arrays, yet still know which members are there's without wasting RAM storing pointers when I already know all of the object instances at compile time.

1

There are 1 best solutions below

2
On BEST ANSWER

You cannot convert/cast (sub)std::array to smaller ones.

std::span(since C++20) (C++20) seems appropriate:

template <std::array<int, 64>& ints,
          std::array<char, 64>& chars,
          std::size_t offset,
          std::size_t size>
struct Temp
{
    std::span<int, size> GetInts() { return std::span<int, size>{ &ints[offset], size}; }
    std::span<char, size> Gethars() { return std::span<char, size>{ &chars[offset], size}; }
};

Dem