考虑固定大小的向量的以下简化和不完整的实现:
template<typename T>
class Vec {
T *start, *end;
public:
T& operator[](ssize_t idx) { return start[idx]; }
void pop() {
end--;
end->~T();
}
template<typename... U>
void push(U... args) {
new (end) T { std::forward<U>(args)... };
end++;
}
};
Run Code Online (Sandbox Code Playgroud)
现在考虑以下T:
struct T {
const int i;
};
Run Code Online (Sandbox Code Playgroud)
以下用例:
Vec<T> v;
v.push(1);
std::cout << v[0].i;
v.pop();
v.push(2);
std::cout << v[0].i;
Run Code Online (Sandbox Code Playgroud)
索引运算符使用start指针来访问对象.此时的对象被销毁,pop并在其存储位置创建了另一个对象push(2).如果我正确阅读了围绕std :: launder的文档,这意味着v[0]下面一行中的行为是未定义的.
如何使用std :: launder来纠正这段代码?每次使用贴片新品时,我们是否必须开始和结束?stdlib的当前实现似乎使用类似于上面发布的代码.这些实现的行为是否未定义?
请考虑以下代码:
#include <atomic>
extern std::atomic<int> i;
void f(void)
{
while (!i.load(std::memory_order_relaxed))
;
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找C++ 11标准的引用,该标准表明编译器不允许将循环转换为
if (!i.load(std::memory_order_relaxed)) {
while (1)
;
}
Run Code Online (Sandbox Code Playgroud)
编辑:这篇文章的先前版本称为循环内部的外部函数.
编辑2:动机:"Effective Java"一书说HotSpot VM执行以下转换:
while (!done)
i++;
Run Code Online (Sandbox Code Playgroud)
至
if (!done)
while (true)
i++;
Run Code Online (Sandbox Code Playgroud)
即使它是另一个线程完美定义的行为,可以同时更改已完成的变量.
假设一个有两个线程(A 和 B)的程序在 Linux 上运行的情况。
int global = 0;
void thread_A(void) {
atomic_store(&global, 1);
udp_send_to_CX();
}
void thread_B(void) {
block_until_udp_recv_from_CX();
int local = atomic_load(&global);
assert(local == 1);
}
Run Code Online (Sandbox Code Playgroud)
线程A向另一台计算机CX发送UDP数据包。收到数据包后,CX 将发送一个 UDP 数据包,该数据包将由线程 B 接收。CX 直到收到线程 A 发送的数据包后才会发送 UDP 数据包。
在发送UDP数据包之前,线程A将值1存储在全局变量中。
线程B收到UDP包后,读取全局变量的值。
我的问题是:
线程 B 中的断言是否保证成功。
也就是说,从 C++ 标准的意义上来说,线程 A 中的存储是否发生在线程 B 中的加载之前。
请注意,我们无法对运行线程 A 和线程 B 的机器做出任何假设。线程 A 发送的 UDP 数据包可能通过与线程 B 中用于接收 UDP 数据包的链路不同的链路发送。
线程 A 和 B 也可能使用不同的套接字来发送和接收数据包。
编辑:针对以下一些评论和答案进行澄清:
例如,如果 Linux 内核中有一个全局原子计数器,在发送数据包之前和接收数据包之后立即通过获取-释放顺序递增,那么问题的答案将是“是的,断言总是成功”。例如send和recv系统调用可以按如下方式实现:
int network_counter …Run Code Online (Sandbox Code Playgroud)