2

I'd like to can create a template function, which can initialize (with placement new) an array, where for each element, a lambda is called to initialize it.

template <typename OBJECT, typename LAMBDA>
void initialize(void *storage, int size, LAMBDA lambda) {
    for (int i=0; i<size; i++) {
        lambda(static_cast<char*>(storage)+i*sizeof(OBJECT), i);
    }
}

Where an actual lambda could be this (for example, an integer array, where each element get initialized to its index):

[](void *storage, int index){ new(storage) int(index); }

And this works. However, as I see, it doesn't create an actual array, because I didn't used a placement-new-array, but separate placement-new calls for each element, even though the memory layout should be the same. Am I right? Can I refer to this memory as an array? If not, can this functionality be achieved somehow?

(Basically, this functionality is the same, if std::vector had a array-generator push_array_back function, which has a size and lambda parameters. And it would push_back an elements of size, initialized to the value lambda gives)

Passer By
  • 19,325
  • 6
  • 49
  • 96
geza
  • 28,403
  • 6
  • 61
  • 135
  • No. Welcome to broken memory model land. – Passer By Dec 05 '17 at 10:05
  • you also need to guarantee proper alignment of the `storage` pointer – Massimiliano Janes Dec 05 '17 at 10:08
  • @MassimilianoJanes Nope, that isn't enough. There is no legal way to for the user to create a `std::vector`-like type – Passer By Dec 05 '17 at 10:10
  • @PasserBy uhm, where did I claimed is 'enough' ?? anyway, correct me if I'm wrong, but if `storage` comes from a properly aligned char array (or any other source guaranteed to produce something equivalent) then the static_cast is legal, pointer arithmetic is legal, placement new is legal, the initialize() call is legal, or not ? (I'm not claiming an 'array' is created, nor that a full vector<> can be implemented in this way) – Massimiliano Janes Dec 05 '17 at 10:19
  • @MassimilianoJanes As pointed out by OP, there is no array object hence using any pointers created from pointer arithmetic on them is UB. This renders the created objects unusable, as you don't store the pointers from the new expressions, and there is no way to acquire them. – Passer By Dec 05 '17 at 10:20
  • @PasserBy, obviously I was talking about the pointer arithmetic in the initialize() function, I was just claiming that the initialize() call is legal under those assumptions; correct me if I'm wrong, but you *can* use the resulting objects as long as you pass through the original char* pointer and perform arithmetic on it ... (say, via an iterator) – Massimiliano Janes Dec 05 '17 at 10:25
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160505/discussion-between-passer-by-and-massimiliano-janes). – Passer By Dec 05 '17 at 11:55
  • @PasserBy: Why do you think that storing pointers from `new` is needed? I've asked [this](https://stackoverflow.com/questions/47653305/is-there-a-semantic-difference-between-the-return-value-of-placement-new-and-t) question, and it seems that it is not required. – geza Dec 05 '17 at 13:11

1 Answers1

0

Am I right? Can I refer to this memory as an array? If not, can this functionality be achieved somehow?

This is basically the same underlying problem that std::vector has with data() and pointed out in CWG 2182. It's undefined behavior to refer to this memory as an array, but practically speaking it needs to work because so much code relies on it working. There's no way to achieve it.


Side-node, don't call your callables lambda. They don't have to be lambdas. And also let the language help you with pointer offsetting:

auto p = static_cast<Object*>(storage);
for (int i = 0; i < size; ++i) {
    construct(static_cast<void*>(p + i), i);
}
Barry
  • 286,269
  • 29
  • 621
  • 977
  • "validity of doing pointer arithmetic to address separately-allocated but contiguous objects in a container like std::vector". Hmm. What could they mean here? `separately-allocated`? Does a `std::vector` do something like this? I've used pointer arithmetic on a `char*` intentionally, because it seemed "less UB" :) Arithmetic on `Object *` is UB, but on char may be not: https://stackoverflow.com/questions/47498585/is-adding-to-a-char-pointer-ub-when-it-doesnt-actually-point-to-a-char-arr – geza Dec 05 '17 at 16:23