【问题标题】:C++ "volatile struct" issue: compiles with gcc 11 but not with earlier versionsC++“volatile struct”问题:使用 gcc 11 编译,但不使用早期版本
【发布时间】:2022-01-13 07:49:21
【问题描述】:

以下代码使用 gcc 11+ 编译,但使用 gcc 版本


#include <stdint.h>

typedef volatile struct s {
    uint32_t receiver:1;
    uint32_t transmitter:1;
    uint32_t _res:30;
} s_t;

s_t get_struct(void){
    struct s m = {0};
    return m;
}

int main() {

    // compiles using gcc 11
    // does not compile using gcc 10 and lower
    s_t io1 = get_struct();

    return 0;
}

您可以在这里亲自尝试: https://godbolt.org/z/cavYxKdKo

你能解释一下这是为什么吗?

仅供参考,如果单个结构成员使用 volatile 限定(而不是结构本身),则代码将使用早期的 gcc 版本编译。我不知道为什么会这样,因为我认为语义是相同的 (at least they are in C)。

typedef struct s {
    volatile uint32_t receiver:1;
    volatile uint32_t transmitter:1;
    volatile uint32_t _res:30;
} s_t;

类似问题:

【问题讨论】:

  • IIRC get_struct 返回一个 s,顶级限定符 (const & volatile) 被忽略。所以s_t io1 = get_struct(); 尝试从s 类型的pr 值初始化volatile s
  • 恕我直言,您要么需要 s 的构造函数,要么需要 volatile sconst_cast 的返回 get_struct
  • 如果您将标准设置为 C++17,则使用 gcc 10.x 编译 - 这会强制您进行复制省略。这可能是 gcc 11.x 中的默认设置。
  • @RichardCritten,谢谢,这似乎指向了正确的方向。 ????因此,带有标准 C++14 (-std=c++14) 的 gcc 11.x 无法编译。但是,我用 gcc 11.x 尝试了-fno-elide-constructors,但确实可以编译。 ???令人费解。无论如何,问题仍然悬而未决,为什么在关闭复制省略时这段代码无法编译?隐式声明的复制构造函数是否与 volatile 限定符不一致?为什么 C++ 如此神秘? ????‍♂️
  • -fno-elide-constructors 不影响强制复制省略。

标签: c++ gcc struct volatile


【解决方案1】:

感谢对我的问题提供帮助的 cmets,我尝试尽我所知自己回答这个问题。如果您发现错误,请发表评论。

代码是否编译与使用的C++版本有关。

给定的代码

  • 使用 C++17 编译(使用 gcc 11 或带有选项 -std=c++17 的 gcc 10)和
  • 不能用 C++14 编译(使用 gcc 10 或 gcc 11 和选项 -std=c++14)。

两个 C++ 版本在此问题上的区别在于从 C++17 开始的“强制省略复制/移动操作”,请参阅 here

使用 C++14,需要定义适当的构造函数才能构造s_t io1

为了更容易看到这一点,在链接的示例中切换到“x64-64 clang 13.0.0)”。这个编译器提供了我认为更容易理解的诊断信息。当给定选项-std=c++17 时,“clang”编译代码。否则,它会发出错误消息:

error: no matching constructor for initialization of 's_t' (aka 'volatile s')

它还列出了几个隐式定义的构造函数,这些构造函数被拒绝为“不可行”。

换句话说,我们需要手动提供正确的构造函数(也就是用户定义的构造函数)。

首先,我们需要注意struct s m = {0}; 的初始化,因为一旦我们添加了任何用户定义的构造函数,就不会再有隐式定义的构造函数了。见default constructor。此外,一旦将用户定义的构造函数添加到结构中,aggregate initialization 就不再适用。

这将为编译执行s(int i) {}(让我们从构造函数中省略初始化或其他代码)。

其次,我们需要为参数类型s_t提供一个构造函数。

如果我声明一个构造函数s(volatile struct s l) {}

“clang”抱怨

error: copy constructor must pass its first argument by reference
    s(volatile struct s  l) {  }

如果我声明一个复制构造函数s(volatile struct s &amp; l) { }

“clang”抱怨

error: no matching constructor for initialization of 's_t' (aka 'volatile s')

但是,如果我声明一个移动构造函数s(volatile struct s &amp;&amp; l) { }

代码编译(使用 clang 和 gcc,C++14 和 C++17)。

这是try for yourself的结果代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 2018-05-13
    相关资源
    最近更新 更多