5

The following code compiles fine:

#include <iostream>
#include <memory>
int main()
{
const int * a = new int(5);

std::cout << *a << std::endl; // Prints 5

// *a = 5; // Compiler error.
using at = std::allocator_traits<typename std::allocator<int>>;
auto alloc = std::allocator<int>();
at::construct(alloc, a);

std::cout << *a << std::endl; // Prints 0
}

Under the hood libstdc++ does

::new((void*)a) int;

but a is const!

Is this undefined behavior? Or does placement new not count as modifying? I modified the value of *a, which is const. To my understanding this is not allowed:

Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.

https://en.cppreference.com/w/cpp/language/const_cast

Andreas Pasternak
  • 1,250
  • 10
  • 18
  • 1
    This looks like a reasonable dupe: https://stackoverflow.com/questions/42997440/is-it-legal-to-use-placement-new-on-initialised-memory. Not 100% sure so I'll leave it you/the community to decide. – NathanOliver Jul 08 '19 at 21:21
  • @NathanOliver: Great! Thank you. Reading trough it. – Andreas Pasternak Jul 08 '19 at 21:26
  • @NathanOliver Whether it is legal to use placement-new on initialized memory seems only part of the question. Whether `std::allocator_traits::construct()` is legal is the other part. – Deduplicator Jul 08 '19 at 21:44

2 Answers2

4

TL;DR: Fine until C++2a, thereafter std::allocator_traits<std::allocator<int>>::construct() will be more demanding of the passed pointer.


Well, std::allocator_traits::construct() uses static_cast<void*> since it was introduced, unless the allocator provides it.

And while std::allocator::construct() was deprecated in C++17 and will be removed in C++2a, it always used a c-style cast.

Thus, it's syntactically valid until C++2a.


And as the pointed-to object itself is not const, only the pointer it is accessed through having that qualifier, casting away const and modifying is perfectly legal.

As the pseudo-dtor for int is trivial, it doesn't even matter that it isn't called before constructing a new one on top of it.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0

const int* a specify that the value contained by the memory slot pointed by a is constant, but not the pointer itself.

#include <memory>

int main()
{
    const int * a = new int;

    a = nullptr; // Compile fine
    *a = 1; // Won't compile

    return 0;
}

You were expecting your code to behave like:

#include <memory>

int main()
{
    int* const a = new int;

    a = nullptr; // Won't compile
    *a = 1; // Compile fine

    return 0;
}
Adrien Givry
  • 956
  • 7
  • 18