问题
C++ reference says: http://en.cppreference.com/w/cpp/atomic/atomic
std::atomic may be instantiated with any TriviallyCopyable type T
However following example does not work under 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;
}
Compile: 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_16collect2: error: ld returned 1 exit status
g++-6 --version
g++ (Ubuntu 6.2.0-7ubuntu11) 6.2.0 20161018
Especially I do not understand why Test2 works but Test3 does not.
Any ideas?
EDIT: added -latomic flag and g++ version
回答1:
As mentioned by @TartanLlama in its now-deleted answer, you need to link against libatomic:
g++-6 -std=c++14 test.cpp -latomic
You need to add -latomic at the end of the compilation line. Some compilers (linkers) may work correctly if you put -latomic before test.cpp (e.g. g++ on Coliru), but some won't (see Why does the order in which libraries are linked sometimes cause errors in GCC?).
Disclaimer: I am not an expert in linkage, so I cannot provide a detailed explanation as to why it works with -latomic before on some platform and not on other (I am guessing the linkers are different, but... ).
As to why your code compiles if you remove Test3, this is compiler and architecture dependent. If you look at the generated ASM with -O2 and g++6.2 on godbolt:
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
You see that for structure that takes less than 8 bytes (Test1X, Test2), the compiler can use the mov QWORD instruction (a qword is usually 8 bytes long on nowadays architectures), but it cannot generate a single instruction to handle the cases where the size is strictly greater than 8 (sizeof(Test3) will usually be 16).
Basically, there is probably a specialization of std::atomic<T> (or of some operations of std::atomic<T>) in g++1 when T is "small", and the definition of "small" is probably architecture dependent.
Disclaimer: Again, I am not an expert regarding <atomic> so this is mainly from experiments regarding generated ASM on godbolt and behaviour of g++ and clang on Coliru.
1clang has a __atomic_store_8 procedure and a __atomic_store procedure, and without -latomic it will not compile for Test2 and Test3. However it manages to compile Test13 even if sizeof(Test13) is 8 so it does not use __atomic_store_8 for some structures. icc has a completely different behavior and does not generate any call (cannot test on Coliru, but you can look it up on godbolt).
来源:https://stackoverflow.com/questions/40739306/stdatomic-trivially-copyable-structs