相关疑难解决方法(0)

std :: launder的目的是什么?

P0137引入了函数模板, std::launder并在有关联合,生命周期和指针的部分中对标准进行了许多更改.

这篇论文解决了什么问题?我必须注意哪些语言的变化?我们在做什么launder

c++ memory c++-faq c++17 stdlaunder

223
推荐指数
3
解决办法
2万
查看次数

这真的打破了严格别名规则吗?

当我使用g ++编译这个示例代码时,我收到此警告:

警告:解除引用类型惩罚指针将破坏严格别名规则 [-Wstrict-aliasing]

代码:

#include <iostream>

int main() 
{
   alignas(int) char data[sizeof(int)];
   int *myInt = new (data) int;
   *myInt = 34;

   std::cout << *reinterpret_cast<int*>(data);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,不是data别名int,因此将其强制转换为int不会违反严格的别名规则?或者我在这里遗漏了什么?

编辑:奇怪,当我这样定义时data:

alignas(int) char* data = new char[sizeof(int)];
Run Code Online (Sandbox Code Playgroud)

编译器警告消失了.堆栈分配是否与严格别名产生差异?事实上它是一个char[]而不是一个char*意味着它实际上不能为任何类型别名吗?

c++ strict-aliasing language-lawyer

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

如何在C++中正确访问映射内存而没有未定义的行为

我一直试图弄清楚如何从C++ 17访问映射缓冲区而不调用未定义的行为.对于这个例子,我将使用Vulkan的返回缓冲区vkMapMemory.

因此,根据N4659(最终的C++ 17工作草案),[intro.object]部分(重点补充):

C++程序中的构造创建,销毁,引用,访问和操作对象.一个目的是通过一种创建定义(6.1),通过一个 新的表达式 (8.3.4)中,当隐式地改变所述一个联合的活性部件(12.3),或当一个临时对象被创建(7.4,15.2).

显然,这些是创建C++对象的唯一有效方法.因此,假设我们得到一个void*指向主机可见(和相干)设备内存的映射区域的指针(当然,假设所有必需的参数都有有效值并且调用成功,并且返回的内存块足够大)正确对齐):

void* ptr{};
vkMapMemory(device, memory, offset, size, flags, &ptr);
assert(ptr != nullptr);
Run Code Online (Sandbox Code Playgroud)

现在,我希望将此内存作为float数组访问.显而易见的事情static_cast是指针并按照我的快乐方式继续如下:

volatile float* float_array = static_cast<volatile float*>(ptr);
Run Code Online (Sandbox Code Playgroud)

(volatile包含它因为它被映射为相干存储器,因此可以在任何时候由GPU写入).然而,在该存储器位置中技术上float不存在阵列,至少不是在引用的摘录的意义上,因此通过这样的指针访问存储器将是未定义的行为.因此,根据我的理解,我有两种选择:

1. memcpy数据

它应该总是能够使用本地缓存,将它转换为std::byte*memcpy表示到映射区域.GPU将按照着色器中的指示解释它(在这种情况下,作为32位数组float),从而解决了问题.但是,这需要额外的内存和额外的副本,所以我宁愿避免这种情况.

2.放置 - new阵列

看来,[new.delete.placement]部分没有对如何获得放置地址施加任何限制(无论实现的指针安全性如何,它都不必是安全派生的指针).因此,可以通过放置创建有效的浮点数组new,如下所示:

volatile …
Run Code Online (Sandbox Code Playgroud)

c++ volatile language-lawyer mapped-memory c++17

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

在 std::launder 之前,所有 std::vector 的实现都是不可移植的吗?

当在实例emplace_back()上调用时std::vector,会在先前分配的存储中创建一个对象。这可以通过 Placement-new 轻松实现,它非常便携。但现在,我们需要访问嵌入的元素而不调用未定义的行为。

这篇文章中 我了解到有两种方法可以做到这一点

  1. 使用placement-new返回的指针: auto *elemPtr = new (bufferPtr) MyType();

  2. 或者,从 C++17 开始,std::launder所转换的指针bufferPtr
    auto *elemPtr2 = std::launder(reinterpret_cast<MyType*>(bufferPtr));

第二种方法可以很容易地推广到这样的情况,即我们有很多对象放置在相邻的内存位置,如std::vector. 但在 C++17 之前人们做了什么?一种解决方案是将placement-new 返回的指针存储在单独的动态数组中。虽然这当然是合法的,但我认为它并没有真正实现 std::vector [此外,单独存储我们已经知道的所有地址是一个疯狂的想法]。另一个解决方案是存储lastEmplacedElemPtrinside std::vector,并从中删除适当的整数 - 但由于我们实际上没有MyType对象数组,这可能也是未定义的。事实上,此 cppreference 页面中的一个示例声称,如果我们有两个相同类型的指针比较相等,并且其中一个可以安全地取消引用,则取消引用另一个可能仍然是未定义的。

那么,在 C++17 之前有没有办法以可移植的方式实现 std::vector 呢?或者也许 std::launder 确实是 C++ 的一个关键部分,当涉及到新的放置时,自 C++98 以来就缺失了?

我知道这个问题表面上与SO上的许多其他问题相似,但据我所知,他们都没有解释如何合法地迭代由placement-new构造的对象。事实上,这一切都有点令人困惑。例如,示例形式的cppreference 文档中的 std::aligned_storage注释 似乎表明 C++11 和 C++17 之间存在一些变化,并且简单的别名违规reinterpret_cast在 C++17 之前是合法的 [无需std::launder]。类似地,在文档 的示例中,std::malloc 他们只是对返回的指针进行指针算术 …

c++ placement-new language-lawyer c++11

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

转换为具有相同数据成员布局但具有不同实现的类是否安全?

第一个类将用于私有继承,以确保完全相同的布局.这应该使铸件安全.

#include <iostream>
#include <string>

struct data_base
{
    data_base( int i, std::string&& s ) noexcept
        : i_{ i }
        , s_{ std::move( s ) }
    {}

    int i_;
    std::string s_;
};
Run Code Online (Sandbox Code Playgroud)

在这个简单的例子,我打印int数据成员第一接着std::string对实例数据成员data<true>.

template<bool = true>
struct data : private data_base // inherits
{
    data( int i, std::string&& s ) noexcept
        : data_base( i, std::move( s ) )
    {}

    void print()
    {
        std::cout << "data<true> - " << i_ << s_ << '\n';
    }
}; …
Run Code Online (Sandbox Code Playgroud)

c++ performance casting type-conversion language-lawyer

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