[英]How can I make a volatile struct behave exactly like a volatile int during assignment?
当一个人从非易失性 int 分配给volatile int
int
时,编译器。 当从相同类型的volatile struct
从易失性struct
分配时,编译器似乎非常不高兴。
考虑以下简单程序。
struct Bar {
int a;
};
volatile int foo;
int foo2;
volatile Bar bar;
Bar bar2;
int main(){
foo = foo2;
bar = bar2;
}
当我尝试编译这段代码时,我在 main 的第二行出现错误,但第一行没有。
g++ Main.cc -o Main
Main.cc: In function ‘int main()’:
Main.cc:13:9: error: passing ‘volatile Bar’ as ‘this’ argument discards qualifiers [-fpermissive]
bar = bar2;
^
Main.cc:1:8: note: in call to ‘Bar& Bar::operator=(const Bar&)’
struct Bar {
问题似乎是因为volatile Bar
被传递到赋值运算符的左侧,尽管我不确定为什么这不是int
的问题。
我查看了这个建议进行以下修复的答案。
struct Bar {
int a;
volatile Bar& operator= (const Bar& other) volatile {
*this = other;
}
};
不幸的是,这导致了以下两个警告。
g++ Main.cc -o Main
Main.cc: In member function ‘volatile Bar& Bar::operator=(const Bar&) volatile’:
Main.cc:4:21: warning: implicit dereference will not access object of type ‘volatile Bar’ in statement
*this = other;
^
Main.cc: In function ‘int main()’:
Main.cc:16:15: warning: implicit dereference will not access object of type ‘volatile Bar’ in statement
bar = bar2;
然后我查看了这个答案,其中提到我应该将引用转换为右值,但我不确定要转换哪个引用,以及在这种情况下使用哪种转换语法。
使main
的第 2 行上的分配行为与main
的第 1 行完全一样,没有警告或错误的正确咒语是什么?
您最初的问题是因为隐式赋值运算符具有签名
Bar& operator=(const Bar& rhs);
...而这对于volatile
对象是不可调用的。 警告是因为更新后的函数返回了易失性引用,但从未使用过该引用。 GCC认为这可能是一个问题。 解决此问题的最简单方法是将返回类型更改为void!
还有另一个问题:您的函数将以无限递归方式进行调用。 我建议以下内容:
struct Bar {
int a;
Bar& operator=(const Bar&rhs) = default;
void operator=(const volatile Bar& rhs) volatile // Note void return.
{
// Caution: This const_cast removes the volatile from
// the reference. This may lose the point of the original
// volatile qualification.
//
// If this is a problem, use "a = rhs.a;" instead - but this
// obviously doesn't generalize so well.
const_cast<Bar&>(*this) = const_cast<const Bar&>(rhs);
}
};
volatile Bar vbar;
Bar bar;
int main(){
vbar = bar; // All four combinations work.
bar = vbar;
vbar = vbar;
bar = bar;
return 0;
}
这意味着在使用易失性结构时,您将无法链接分配运算符。 我断言这不是很大的损失。
最后一点:为什么要使用volatile
它对多线程代码不是很有用(它对内存映射IO很有用)。
@martin 很好地描述了问题发生的原因,但没有提供不会消除易变性的通用解决方案。
如果放弃 volatile 没问题,那么应该在赋值之前在函数外部完成。 将它隐藏在复制构造函数中会留下更多不可预见的错误潜入的可能性。
稍微调整解决方案,给出以下更通用的解决方案:
struct Bar {
int a;
Bar& operator=(const Bar&rhs) = default;
auto & operator=(const volatile Bar& rhs) volatile
{
// Solution 1: depends on struct definition and can't be copied to many
static_assert(sizeof(*this) == sizeof(a), "struct elements added or changed. update below");
a = rhs.a;
// Solution 2: very general solution that could be used if copying to many structs
// Do default byte-by-byte copy operation, but can't use memcpy for volatile
static_assert(is_trivially_copy_assignable_v<remove_cvref_t<decltype(*this)>>>, "Byte-by-byte copy may not be possible with this class");
std::copy_n(reinterpret_cast<const volatile std::byte*>(&rhs), sizeof(*this), reinterpret_cast<volatile std::byte*>(this));
// Support chaining of assignments
//return *this; // Okay, but returning a volatile will generate spurious warnings if that value is not read afterword
return rhs; // assumes that you want to just chain assignments and not do anything weird
}
};
此版本的复制构造函数适用于易失性/非易失性源和目标的所有 4 种组合,但是,如果您希望针对每种情况进行优化,您也可以拆分为不同的组合。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.