标签: stdlaunder

我可以通过placement-new覆盖const对象吗?

基本生活/8告诉我们,我们可以在一个对象的生命周期结束后,使用它所占用的存储空间来创建一个新的对象,并使用它原来的名称来引用它,除非:

\n
\n
    \n
  • 原始对象的类型不是 const 限定的,并且,如果是类类型,则不包含任何类型为 const 限定的非静态数据成员或引用类型,并且 [...]
  • \n
\n
\n

强调我的

\n

但是,就在其下方,我们可以看到一条注释:

\n
\n
    \n
  • 如果不满足这些条件,则可以通过调用从表示其存储地址的指针获得指向新对象的指针std\xe2\x80\x8b::\xe2\x80\x8blaunder
  • \n
\n
\n

这解释了的目的std::launder。我们可以有一个类类型const,并使用placement-new 来创建一个具有不同内部值的新对象。

\n

让我惊讶的是第一句话的第一部分。它清楚地表明,如果存储是const(不一定包含const成员,但整个对象被声明为const),我们不能用它来引用一个新对象,但这可能意味着std::launder可以修复它。

\n

但我们如何创建这样的对象呢?最简单的例子失败了:

\n
const auto x = 5;\nnew (&x) auto(10);\n
Run Code Online (Sandbox Code Playgroud)\n

这是因为我们不能用作const void*新放置的缓冲区。我们可以const_cast做到,但抛弃“真实”const性就是未定义的行为。我相信这里也是如此。

\n

如果只是禁止使用const对象作为放置新缓冲区,我会理解,但如果是这样,那么第一个引用中强调的部分的目的是什么?我们能否真正利用重用const对象的存储重用于不同的对象吗?

\n

c++ language-lawyer stdlaunder

6
推荐指数
1
解决办法
399
查看次数

std::launder 与放置 - 新的可达性条件

std::launder有一个先决条件:从将要返回的指针可到达的所有字节都可以通过传递的指针到达。

我的理解是,这是为了允许编译器优化,以便例如

struct A {
    int a[2];
    int b;
};

void f(int&);

int g() {
    A a{{0,0},2};
    f(a.a[0]);
    return a.b;
}
Run Code Online (Sandbox Code Playgroud)

可以优化为始终返回2。(请参阅指针可互换性与具有相同地址能否使用 std::launder 将对象指针转换为其封闭数组指针?


这是否意味着可达性先决条件也应该适用于placement-new?否则有什么阻止我写f如下吗?:

void f(int& x) {
    new(&x) A{{0,0},0};
}
Run Code Online (Sandbox Code Playgroud)

的地址与 的地址a.a[0]相同,a并且新A对象可以透明地替换为旧A对象,因此a.bing现在应该是0

c++ placement-new language-lawyer stdlaunder

6
推荐指数
0
解决办法
382
查看次数

为什么这里需要 std::launder ?

我正在阅读 cppreference ,在std::aligned_storage的示例中有一个向量/数组类的示例:

template<class T, std::size_t N>
class static_vector
{
    // properly aligned uninitialized storage for N T's
    typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
    // IF you want to see possible implementation of aligned storage see link.
    // It's very simple and small, it's just a buffer
    
    std::size_t m_size = 0;
 
public:
    // Create an object in aligned storage
    template<typename ...Args> void emplace_back(Args&&... args) 
    {
        if( m_size >= N ) // possible error handling
            throw std::bad_alloc{};
 
        // construct value in memory …
Run Code Online (Sandbox Code Playgroud)

c++ strict-aliasing undefined-behavior stdlaunder

5
推荐指数
1
解决办法
954
查看次数

std::launder 的效果是否在调用它的表达式之后持续?

考虑以下示例代码:

struct X { const int n; };
union U { X x; float f; };
void fun() {
  U u = {{ 1 }};
  u.f = 5.f;               // OK, creates new subobject of 'u'
  X *p = new (&u.x) X {2}; // OK, creates new subobject of 'u'
  
  if(*std::launder(&u.x.n) == 2){// condition is true because of std::launder
    std::cout << u.x.n << std::endl;  //UB here?
    }
}
Run Code Online (Sandbox Code Playgroud)

函数会fun根据语言标准打印什么?换句话说,std::launderlast 的效果是否超出了它被调用的表达式?std::launder或者,我们每次需要访问更新后的值时都必须使用u.x.n?

c++ compiler-optimization language-lawyer stdlaunder

5
推荐指数
1
解决办法
119
查看次数

如何解释 std::launder 的前提条件?

struct X { int n; };
const X *p = new const X{3};  // #1
new (const_cast<X*>(p)) const X{5};  // #2
const int c = std::launder(p)->n; 
Run Code Online (Sandbox Code Playgroud)

假设在 处创建的对象#1名为 ,obj1而在 处创建的对象#2名为obj2。的前提std::launder

[ptr.launder] p2链接

p代表内存中一个字节的地址A。处于其生命周期内且类型与 T 类似的对象 X 位于地址 A 处。通过结果可访问的所有存储字节都可通过 p (见下文)访问

如果存在对象 Z,则可以通过指向对象 Y 的指针值访问存储 b 的字节,并且该对象可与 Y 进行指针互换,这样 b 位于 Z 占用的存储空间内,或者如果 Z 则位于直接封闭的数组对象内是一个数组元素。

这个规则有点晦涩难懂。以下解释正确吗?

obj2sizeof(X)将占用以 开头的字节数A。将Y(指向的对象std::launder(p)) 和 …

c++ language-lawyer stdlaunder

5
推荐指数
1
解决办法
225
查看次数

使用 std::launder 以不正确的方式构造类或结构

假设您有一个structwith 元素,您不想(不能)在初始化阶段初始化它,但它不是默认可初始化的。我想出了一种使用方法std::launder:为结构分配一个空间,然后使用洗衣槽在正确的位置分配一个值。例如,

神箭

#include <cassert>
#include <cstddef>
#include <iostream>
#include <new>

struct T {
    int t;
};

struct S {
    int x;
    T t;
    S() = delete;
    S(int x) : x(x) {}
};

int main() {
    alignas(S) std::byte storage[sizeof(S)];
    S s0(42);
    
    // 0. I believe that the commented line is dangerous, although it compiles well.
    // *std::launder(reinterpret_cast<S*>(storage)) = s0;
    
    S *ps = std::launder(reinterpret_cast<S*>(storage));
    ps->x = 42;
    ps->t = T{};

    { // 1. Is this safe?
        S …
Run Code Online (Sandbox Code Playgroud)

c++ stdlaunder

3
推荐指数
1
解决办法
72
查看次数

std::launder 在带有 -O3 选项的 gcc 上不起作用?

我认为我的代码片段不应产生断言失败。但是使用带有 O3 选项的 gcc 13.2 会失败。

所以我认为这里是未定义的行为。clang 和 gcc 与 O0 工作得很好。如果我将析构函数更改为其他方法 - 断言失败就会消失。

#include <cstddef>
#include <new>
#include <cassert>

struct Foo
{
    ~Foo()
    {
        destructed = true;
    }

    bool destructed = false;
};

int main()
{
    alignas(alignof(Foo)) std::byte buffer[sizeof(Foo)];

    Foo* p1 = std::launder(reinterpret_cast<Foo*>(buffer));

    new (p1) Foo();

    assert(p1->destructed == false);

    Foo* p3 = std::launder(reinterpret_cast<Foo*>(buffer));
    p3->~Foo();
    
    //////////// Assertion failure ?????????????????????????
    assert(p3->destructed == true);
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

c++ undefined-behavior c++17 stdlaunder

1
推荐指数
1
解决办法
81
查看次数