std::atomic 简单可复制的结构

std::atomic trivially copyable structs

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

std::atomic may be instantiated with any TriviallyCopyable type 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>('');
    test<Test13>(1L);
    test<Test2>('',2);
    test<Test3>(1,2L);
    return 0;
}

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

/tmp/cchademz.o: In function std::atomic<Test3>::store(Test3, std::memory_order): test.cpp:(.text._ZNSt6atomicI5Test3E5storeES0_St12memory_order[_ZNSt6atomicI5Test3E5storeES0_St12memory_order]+0x3e): undefined reference to __atomic_store_16 collect2: error: ld returned 1 exit status

g++-6 --version

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

特别是我不明白为什么 Test2 有效而 Test3 无效。

有什么想法吗?

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

正如@TartanLlama 在现已删除的 answer 中提到的,您需要 link 反对 libatomic:

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

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

免责声明:我不是link年龄方面的专家,所以我无法详细解释为什么它适用于-latomic某些平台而不是其他平台(我猜 link 用户不同,但是...)。


至于为什么删除 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 是 "small","small" 的定义可能与体系结构有关。

免责声明: 同样,我不是 <atomic> 方面的专家,所以这主要来自关于在 godbolt 上生成的 ASM 和 g++ 的行为的实验,以及clang 在 Coliru 上。

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