【问题标题】:std::atomic trivially copyable structsstd::atomic 可简单复制的结构
【发布时间】:2016-11-22 10:23:38
【问题描述】:

C++ 参考说:http://en.cppreference.com/w/cpp/atomic/atomic

std::atomic 可以用任何 TriviallyCopyable 类型 T 实例化

但是以下示例在 g++ 6.2.0 下不起作用

#include <atomic>
#include <functional>

struct Test11 {
    int x;
};
struct Test12 {
    char x;
};
struct Test13 {
    long x;
};
struct Test2 {
    char x;
    int y;
};
struct Test3 {
    int y;
    long x;
};

template<typename T, typename... ARGS>
void test(ARGS&& ... args) {
    static_assert(std::is_trivially_copyable<T>::value);

    std::atomic<T> a;
    a.store(T{std::forward<ARGS>(args)...});
}

int main() {
    test<Test11>(1);
    test<Test12>('\1');
    test<Test13>(1L);
    test<Test2>('\1',2);
    test<Test3>(1,2L);
    return 0;
}

编译:g++-6 -std=c++14 -latomic test.cpp

/tmp/cchademz.o:在函数std::atomic&lt;Test3&gt;::store(Test3, std::memory_order): test.cpp:(.text._ZNSt6atomicI5Test3E5storeES0_St12memory_order[_ZNSt6atomicI5Test3E5storeES0_St12memory_order]+0x3e): 未定义引用__atomic_store_16 collect2:错误:ld 返回 1 个退出状态

g++-6 --version

g++ (Ubuntu 6.2.0-7ubuntu11) 6.2.0 20161018

尤其是我不明白为什么Test2 有效,而Test3 无效。

有什么想法吗?

编辑:添加了 -latomic 标志和 g++ 版本

【问题讨论】:

  • 您是否尝试在编译行末尾偶然添加-latomic?代码在 coliru 上编译得很好 -latomic (g++6.2): coliru.stacked-crooked.com/a/fd421bd3d1715897
  • 您可以尝试包含 吗?
  • -latomic 移动到最后工作。但为什么呢?
  • @WaeCo Test3 是唯一一个大小大于8 的结构(在标准架构上),因此编译器可能不需要特定的函数来存储大小小于的原子结构8 个字节(std::atomic&lt;&gt;::store 可能专门用于小类型)。
  • @WaeCo 通常,如果您检查生成的 ASM(例如在 godbolt 上),您会看到最多 8 个字节,编译器使用 mov QWORD ... 指令,但没有指令一次移动超过 8 个字节,这就是您需要自定义函数的原因。

标签: c++ c++11 g++ c++14 atomic


【解决方案1】:

正如@TartanLlama 在其现已删除的answer 中所述,您需要链接到libatomic

g++-6 -std=c++14 test.cpp -latomic

您需要在编译行末尾添加-latomic。如果您将-latomic 放在test.cpp 之前(例如Coliru 上的g++),某些编译器(链接器)可能会正常工作,但有些则不会(请参阅Why does the order in which libraries are linked sometimes cause errors in GCC?)。

免责声明:我不是链接方面的专家,所以我无法详细解释为什么它之前在某些平台上而不是在其他平台上与 -latomic 一起工作(我猜是链接器是不同的,但是...)。


至于如果您删除 Test3,您的代码为何会编译,这取决于编译器和架构。如果您在 godbolt 上查看带有 -O2g++6.2 的生成的 ASM:

sub     rsp, 24
movabs  rax, 8589934593
mov     ecx, 5
mov     DWORD PTR [rsp], 1
mov     rdi, rsp
mov     esi, 1
mov     edx, 2
mfence
mov     BYTE PTR [rsp], 1
mfence
mov     QWORD PTR [rsp], 1
mfence
mov     QWORD PTR [rsp], rax
mfence
call    __atomic_store_16

您会看到,对于占用少于 8 个字节的结构(Test1XTest2),编译器可以使用 mov QWORD 指令(在当今的架构上,qword 通常为 8 个字节长),但它无法生成一条指令来处理大小严格大于 8 的情况(sizeof(Test3) 通常为 16)。

基本上,当T为“小”时,g++1中可能存在std::atomic&lt;T&gt;的特化(或std::atomic&lt;T&gt;的某些操作),而“ small" 可能取决于架构。

免责声明:再说一次,我不是&lt;atomic&gt; 方面的专家,所以这主要来自关于在godbolt 上生成的ASM 以及g++clang 在Coliru 上的行为的实验。

1clang 有一个__atomic_store_8 过程和一个__atomic_store 过程,并且没有-latomic 它不会为Test2Test3 编译。但是,即使 sizeof(Test13) 为 8,它也会设法编译 Test13,因此它不会将 __atomic_store_8 用于某些结构。 icc 具有完全不同的行为,不会生成任何 call(无法在 Coliru 上测试,但您可以在 Godbolt 上查找)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-27
    • 2015-03-03
    • 2017-05-17
    • 2012-03-22
    • 1970-01-01
    • 2019-06-12
    • 2018-11-09
    • 1970-01-01
    相关资源
    最近更新 更多