c++ - 将值分配给必须是可平凡复制的 volatile 结构
问题描述
我有一个struct
需要声明一些实例的实例volatile
,因为它们代表与驱动程序共享的内存(即内存可能被我的 C++ 程序之外的进程更改)。也需要是可简单复制的struct
,因为我将与一些代码共享它的实例,这些代码要求其所有输入都是可简单复制的。这两个要求似乎意味着我不可能安全地volatile
为struct
.
这是我正在尝试做的一个简化示例:
struct foo {
uint16_t a;
uint16_t b;
};
int main() {
static_assert(std::is_trivially_copyable<foo>::value, "Oh no!");
volatile foo vfoo;
foo foo_value{10, 20};
vfoo = foo_value;
}
如果我尝试用 g++ 编译它,它会在vfoo = foo_value
“错误:将 'volatile foo' 作为 'this' 参数丢弃限定符”的行上失败。根据这个问题,这是因为隐式定义的赋值运算符未声明为易失性,我需要定义一个易失性复制赋值运算符才能分配给易失性对象。但是,如果我这样做:
struct foo {
uint16_t a;
uint16_t b;
volatile foo& operator=(const foo& f) volatile {
if(this != &f) {
a = f.a;
b = f.b;
}
return *this;
}
}
然后静态断言失败,因为foo
如果它具有用户定义的赋值运算符,则不再是可简单复制的。
由于编译器显然已决定不允许我执行此非常简单的操作,因此我目前正在使用此解决方法:
int main() {
static_assert(std::is_trivially_copyable<foo>::value, "Oh no!");
volatile foo vfoo;
foo foo_value{10, 20};
memcpy(const_cast<foo*>(&vfoo), &foo_value, sizeof(foo));
std::atomic_signal_fence(std::memory_order_acq_rel);
}
显然,抛弃volatile
不是一个好主意,因为这意味着现在允许编译器违反volatile
应该强制执行的语义(即代码中的每次读取和写入都被转换为对内存的实际读取或写入)。我试图通过用 a 替换分配来缓解这种情况memcpy
,这应该意味着编译器无法优化写入(即使它认为写入对程序的其余部分不可见),并通过添加内存栅栏在赋值之后,这应该意味着编译器不能选择将写入延迟到很久以后。
这是我能做的最好的吗?是否有更好的解决方法可以更接近正确的语义volatile
?或者有没有办法让编译器让我为volatile
结构分配一个新值,而不会使结构不可复制?
解决方案
如果您不一定需要使用赋值运算符(即,如果您认为 memcpy 替代方案可行),那么您可以编写一个非运算符赋值函数:
volatile foo& volatile_assign(volatile foo& f, const foo& o) {
if(&f != &o) {
f.a = o.a;
f.b = o.b;
}
return f;
}
如果您愿意,可以使用成员函数。
我根据您的示例编写了此代码,但请考虑自分配检查对于易失性语义是否有效。不应该重写相同的值吗?我认为这种情况甚至无效,除非对象实际上是非易失性的,否则我们将通过非易失性引用读取易失性对象(也许您也需要对另一个操作数进行 volatile 限定)。
推荐阅读
- javascript - 如何从 NativeScript 中的 JSON 数组在页面上创建元素?
- java - @ControllerAdvice 仅用于集中/全局异常处理或我们可以用它做的任何其他事情?
- html - Bootstrap 4 Navbar Toggle 不起作用(粘贴在下面的代码)
- c# - 运行同一表单的多个实例时出现问题
- php - 如何将数组的格式更改为集合作为laravel中的每个项目
- c++ - 如何使用 GCC 选项 -iprefix 和 -iwithprefix?
- javascript - ASP.net 使用 Eval 将逗号分隔的字符串作为参数传递
- javascript - 编写对象的静态数组
- javascript - 添加购物车中产品的总和
- abap - 异常 CX_SY_REF_IS_INITAL