2

In the implementation and in a follow example of std::uninitialised_fill() in cppreference.com I am having some trouble in understanding a couple of things:

template<class ForwardIt, class T>
  void uninitialized_fill(ForwardIt first, ForwardIt last, const T& value)
  {
   typedef typename std::iterator_traits<ForwardIt>::value_type Value;
   ForwardIt current = first;
      try {
       for (; current != last; ++current) {
        ::new (static_cast<void*>(&*current)) Value(value);
       }
     ....
  }

I don't understand why in the static_cast I need to do (&*)?

Stroustrup in his book states "the curious construct (&*) takes care of iterator that are not pointers. In that case we need to take the address of the element obtained by dereference to get a pointer".

I have three questions

  1. What does it he mean "Iterators that are not pointers"? What else could they be other than a generalisation of pointers? More confusing, we need to take address of element obtained by dereferenced to get a pointer.
  2. Syntactically ::new and new are the same. There is a particular reason using ::?
  3. Is get_temporary_buffer() to allocate storage conceptually the same as MyClass * p3 = (MyClass*) ::operator new (sizeof(MyClass)); or are they two different things?

Please could you provide a little example for all questions?

MaxMarcucci
  • 175
  • 9
  • 1
    `current` is not necessarily a pointer. Casting something that's not a pointer to `void *` doesn't usually work out so well. – chris Aug 07 '14 at 13:54
  • An iterator can be implemented as a pointer or a proxy class with some specified requirements (see here for more info: http://en.cppreference.com/w/cpp/concept/Iterator) – Felix Glas Aug 07 '14 at 14:08
  • It should now probably use `std::addressof(*current)` rather than `&*current` also. – Simple Aug 07 '14 at 15:16

2 Answers2

0

I'm not sure if this is what you meant, but you actually have a valid point.

Arguably, a container only contains initialized objects, so if you're initializing an uninitialized buffer, that buffer is almost certainly going to be an array that you would use raw pointers for.

Nevertheless, it's trivial to make iterators that point to uninitialized buffers that are not arrays, so I suppose this was meant to take care of that case.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • *"Arguably, a container only contains initialized objects,"* You can relatively easily write an allocator that default-initializes (instead of value-initialization) or that does not perform any initialization. – dyp Aug 07 '14 at 19:17
  • @dyp: If you did that, then how would the container e.g. copy or move its elements? Wouldn't that be UB? – user541686 Aug 07 '14 at 19:18
  • *"Wouldn't that be UB?"* For default-initialization, no; for no initialization, I think that depends. You do some things with [indeterminate values](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#616). -- Even if it is UB, you could choose to use such a weird container in certain niches. For example, a vector or list with a large (fixed) size but without the overhead of initialization. – dyp Aug 07 '14 at 19:21
  • @dyp: Is it even *possible* to write a `void construct(T *p, ...)` that default-initializes `*p`? – user541686 Aug 07 '14 at 19:23
  • @dyp: Oh I see. In any case though, that's why I said it's arguable. I think it's hard to deny that containers were *meant* to be used only with initialized objects, given how much flexibility they have in copying/moving the objects. Maybe you can find a way to get around that in some cases, but I don't think that was the intention -- not to mention that it gets rid of the exception-safety that containers provide, because now you have to manually destroy the elements as well. – user541686 Aug 07 '14 at 19:30
  • @dyp: Also note that if they really wanted to support "niche" use cases, they would have made an overload of `uninitialized_fill` that took an allocator as an argument. But they didn't, so I really don't get the impression that that was the intention. Maybe you'd interpret it differently, but that's my argument. Heck, I just realized -- with the method you just mentioned, wouldn't you **need** an allocator argument? If you're going to just do `new (p) T;` to default-initialize then `uninitialized_fill` will do the same, so it doesn't look to me like that's going to get you anywhere either! – user541686 Aug 07 '14 at 19:30
  • Well my original criticism was that default-initialization for (essentially) PODs means that no initialization is performed. For non-POD class objects, you're right that this causes all kinds of problems. -- You're right that there should be an overload that takes an allocator, but I guess that there are not many useful examples where an allocator's `construct` function does something different from placement-new (not considering this lead to another defect in the container exception specifications). – dyp Aug 07 '14 at 19:37
  • *"If you're going to just do new (p) T; to default-initialize then uninitialized_fill will do the same, so it doesn't look to me like that's going to get you anywhere either!"* Not sure what you mean. `new (p) T` is different from `new (p) T(x)`, especially for objects that are trivially default-constructible (for which default construction does not do anything). – dyp Aug 07 '14 at 19:38
  • @dyp: sorry I totally forgot that's new(p) T(x), ignore that comment there. – user541686 Aug 07 '14 at 20:10
0
  1. Iterators can be 'proxy classes' like vector<T>::iterator. They can hide pointer inside and override operator * () - see http://en.cppreference.com/w/cpp/iterator
  2. ::new ensures calling 'placement new' from global scope (not std::new)
  3. get_temporary_buffer<T>() returns pair<T*,ptrdiff_t> with size 'allocated', which can be less than requested. The storage space can be different as it is designed for temporary buffers for algorithms (not to polute heap and/or cause stack overflow).
firda
  • 3,268
  • 17
  • 30