-
Couldn't load subscription status.
- Fork 388
bump to c++23 #2330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
bump to c++23 #2330
Conversation
Since C++23 std::unique_ptr<T> is constexpr See: https://en.cppreference.com/w/cpp/memory/unique_ptr.html
|
Regarding Thanks compiler:) |
|
Love it! |
|
Ok, looks like the |
|
C++ distinguishes addresses from objects at a conceptual (compile-time) level. Objects have lifetimes, which just /happen/ to exist at addresses. This matters when it comes to optimizations. Suppose you do something like int *a1 = malloc(sizeof(int));
int *a2 = a1;
*a1 = 42;
printf("%d\n", *a2); // 42
a1 = realloc(a1, 16*sizeof(int)); // assuming we get the same address back
*a1 = 1337;
printf("%d\n", *a2); // undefined behaviourThe first print statement is guaranteed to be 42, but Instead, if we did a1 = realloc(a1, 16*sizeof(int)); // assuming we get the same address back
*a1 = 1337;
a2 = std::launder(a2);
printf("%d\n", *a2); // this is fine now :)we are no longer relying on undefined behaviour. With Of course, |
Wait - if you are getting the same pointer back here, which your explanation depends on - I don't see why this is UB. A) You get the same pointer back. Now a1 == a2. They are both #include <cstdlib>
#include <cstdint>
#include <cstdio>
#include <new>
int main()
{
int *a1 = (int *)malloc(sizeof(int));
int *a2 = a1;
*a1 = 42;
printf("%d\n", *a2); // 42
a1 = (int *)realloc(a1, 16 * sizeof(int)); // We have to cast to int*, otherwise the assignment won't work.
*a1 = 1337;
printf("First addr: 0x%p Second addr: 0x%p \n", a1, a2);
a2 = std::launder(a2); // No effect?
printf("%d\n", *a2); // undefined behaviour if the pointer changed - the old pointer is freed.
}Compiling this locally with I think you need to find a different example here. And the original warning I assume you got, which prompted you to introduce launder in the first place, would be very helpful. I think placement new might be more relevant, but not sure. |
Because the compiler has no guarantee that the same address is referring to the same object. We know it is, this is what we intended; but int *addr = malloc(sizeof(int));
int *bananas = addr;
*bananas = 42;
printf("%d\n", *addr); // 42
int *apples = addr; // this being up here causes UB
addr = (int*) realloc(addr, sizeof(int)); // even assuming the same address, it might not refer to the same object
// int *apples = addr; // if it was down here it'd be fine
// addr = std::launder(addr); // or use launder instead
*apples = 1337;
printf("%d\n", *addr);
You're right in that
We are using placement-new, this is not a concern. We are in control of the address returned. If it's not clear, I can draw up a placement-new example, but the syntax is a bit more awkward than simply using |
#include <new>
#include <cstdio>
#include <print>
struct Count { int n; };
int main() {
const Count* counter = new const Count{3};
int apples = counter->n;
new (const_cast<Count*>(counter)) const Count{5};
int bananas = counter->n; // UB
std::println("apples={}", apples);
std::println("bananas={}", bananas);
}$ clang++ -Wall -Wextra -Wpedantic -O3 -std=c++23 -fsanitize=address,undefined main.cpp
$ ./a.out
apples=3
bananas=5
=================================================================
==7668==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x562bad2c87c1 in operator new(unsigned long) (/home/maz/nyaa/launder/a.out+0x15e7c1)
#1 0x562bad2c9c6d in main (/home/maz/nyaa/launder/a.out+0x15fc6d)
SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s). |
The warning which made me look into this is Related: https://www.think-cell.com/assets/en/career/talks/pdf/think-cell_talk_lifetime.pdf |
|
This example is only leaking sometimes for me, lol #include <new>
#include <cstdio>
#include <print>
int main() {
const int* ptr = new const int(11); // provenance A
std::destroy_at(ptr);
void* raw = const_cast<void*>(static_cast<const void*>(ptr));
int* new_ptr = ::new (raw) int(42); // provenance B
std::print("*new_ptr = {}\n", *new_ptr); // okay
std::print("*ptr = {}\n", *ptr); // UB
std::print("*ptr = {}\n", *std::launder(ptr)); // okay
}Interesting bit: if I use
|
It works! All tests are passing!