#Custom allocator and vector::emplace_back requires construct?

48 messages · Page 1 of 1 (latest)

loud flower
#
#include <vector>
#include <cstdio>

struct item
{
    char a;
    int b;
};

template <typename T>
class Allocator
{
public:
    using value_type = T;
    using size_type = std::size_t;

public:
    Allocator(T* buffer, std::size_t size)
        : buffer_(buffer)
        , size_(size)
    {}

    value_type* allocate(size_type n, const void* hint = 0) { return buffer_; }
    void deallocate(T* p, size_type n) {}

private:
    T* buffer_;
    std::size_t size_;
};

int main()
{
    item buffer[10] = {0};
    Allocator<item> alloc(&buffer[0], sizeof(buffer));

    std::vector<item, Allocator<item>> items(alloc);

    {
        item i = { 'a', 1};
        items.emplace_back(i);
    }

    {
        item i = { 'b', 2};
        items.emplace_back(i); 
    }

    //items.emplace_back('c', 3);

    for (const char* p = reinterpret_cast<const char*>(buffer), *pend = p + (10 * sizeof(int)); p < pend; ++p) {
        printf("%02x ", *p);
    } 
    printf("\n");
}

Having a play with custom allocators.
The first two items are emplaced fine, but ideally one would use usual form which I have added to the program in a commented-out form.
If I enable that line, I get compiler errors.

rugged stumpBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

loud flower
paper yoke
#

you just need to set the language version to C++20

#

the problem is that item is an aggregate

loud flower
#

Ah, that's the kicker.
I need to stay on C++14.
#embedded

paper yoke
#

there's no constructor emplace could call

#

this is unrelated to your allocator

paper yoke
loud flower
#

That simple?

paper yoke
#

the problem is that emplace does T(args...)

#

but item doesn't support () init

#

it only supports {} init

#

but emplace can't use {} because that would potentially trigger an initializer_list ctor

#

so emplace uses ()

#

but that doesn't work with aggregates pre C++20

#
struct item
{
    char a = 0;
    int b = 0;

    item() = default;
    item(char a, int b) : a(a), b(b) {}
};
#

for example

loud flower
#

One guess what I just added to my code 😀

#

Yeah, adding constructors is not a massive upheaval. If you want to use emplace_back().
I oversimplified my MRE, and that bit me.

rugged stumpBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity

paper yoke
#

as i said, C++20 fixed this

#

but yeah, if you're stuck on 14 then you need a ctor

loud flower
#

It is fair to assume that this little program uses no heap memory, right?
I do wonder if there is anything I can do to check.

For instance, what happens if I wrote:

std::vector<item, Allocator<item>> items(alloc);
items.reserve(10);

Does that (pointlessly) reserve memory in that static buffer?
Or would that grab heap still?

paper yoke
#

well, if your allocator doesn't alloc on the heap then this won't use the heap

#

at least not for the vector that uses that allocator ^^

loud flower
#

Thanks for the help.
Much appreciated.

paper yoke
#

np

glad stag
paper yoke
#

well, you don't want

v.emplace_back("bli", "bla")
```to call an initializer_list ctor?
#

that would just be incredibly unintuitive

glad stag
#

oh yeah boog

#

that would be unfortunate

paper yoke
#

especially when it would end up picking the initializer_list ctor over another one

#

like, imagine above example ends up calling T(std::initializer_list<const char*>) instead of T(const char*, const char*)

#

std::initializer_list was a mistake -.-

glad stag
#

yeah i see but then follow-up question: how do you call, if at all, an initializer_list ctor from emplace back? do you just pass a brace-enclosed list of arguments?

paper yoke
#

i don't think you can

glad stag
#

okay, well thanks for clearing that up, I recently had trouble over a similar situation

chrome bramble
#

something akin to

template <typename ... ArgsT>
std::enable_if_t<std::is_aggregate_v<T> > construct(T* p, ArgsT&& ... args)
{
  new(p) T{ std::forward<ArgsT>(args)... };
}
chrome bramble
#

or I guess in general reallocation would be cursed

#

say you have a type that isn't trivial, and a vector with a capacity for 4 elements, some of which are alive

#

then you trigger a reallocation, now the "new, larger" block of memory overlaps the block that is in use and that contains non-trivial objects

#

that'll be an UB galore when moving/copying the objects

#

(yes I'm aware this isn't finale/you're in the process of experimenting to figure things out, but since you asked what would happen I'd argue that's fair game)