The following code crashes with clang (version 5.0.0-3~16.04.1 on x86_64-pc-linux-gnu) but works fine with gcc (9.2.0).
struct Registry {
static int registerType(int type) {
std::cout << "registering: " << type;
return type;
}
};
template<typename T>
struct A {
static int i;
};
template<typename T>
int A<T>::i = Registry::registerType(9);
int main() {
std::cout << A<int>::i << std::endl;
}
The clang crash, is according to address sanitizer due to:
ASAN:DEADLYSIGNAL
=================================================================
==31334==ERROR: AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8 (pc 0x7f5cc12b0bb6 bp 0x7ffdca3d1a20 sp 0x7ffdca3d19e0 T0)
==31334==The signal is caused by a READ memory access.
#0 0x7f5cc12b0bb5 in std::ostream::sentry::sentry(std::ostream&) /root/orig/gcc-9.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream.tcc:48:31
#1 0x7f5cc12b11e6 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) /root/orig/gcc-9.2.0/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream_insert.h:82:39
#2 0x4197a7 in __cxx_global_var_init.1 (/tmp/1576534654.656283/a.out+0x4197a7)
#3 0x514eac in __libc_csu_init (/tmp/1576534654.656283/a.out+0x514eac)
#4 0x7f5cc02847be in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:247
#5 0x419858 in _start (/tmp/1576534654.656283/a.out+0x419858)
Edit
Following the accepted answer, the question can be rephrased to:
ostream
object std::cout
is not properly initialized?std::cout
initialized, even though we included iostream and we use std::cout
properly?cout << "foo"
is not a compiler bug?To avoid the spoiler I would just hint that the answer is Yes . This can happen, but don't worry there is a workaround. To see more follow the accepted answer below .
Also following the accepted answer, the case in question can be narrowed to an even more basic scenario:
int foo() {
std::cout << "foo";
return 0;
}
template<typename T>
struct A {
static int i;
};
template<typename T>
int A<T>::i = foo();
int main() {
(void) A<int>::i;
}
that crashes on the said clang version (and as it seems, justifiably!).
The code unfortunately has unspecified behavior. The reason is similar to, if not the usual definition of, the Static Initialization Order Fiasco.
The object std::cout
and other similar objects declared in <iostream>
may not be used before the first object of type std::ios_base::Init
is initialized. Including <iostream>
defines (or acts as though it defines) a non-local object of that type with static storage duration ([iostream.objects.overview]/3 ). This takes care of the requirement in most cases, even when std::cout
and friends are used during dynamic initialization, since that Init
definition will normally be earlier in the translation unit than any other non-local static storage object definition.
However, [basic.start.dynamic]/1 says
Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, ....
So although the initialization of the std::ios_base::Init
object (effectively) defined in <iostream>
is ordered, the initialization of A<int>::i
is unordered, and therefore the two initializations are indeterminately sequenced. So we can't count on this code working.
As @walnut mentioned in a comment, the code can be corrected by forcing another std::ios_base::Init
object to be initialized during dynamic initialization of A<int>::i
before the use of std::cout
:
struct Registry {
static int registerType(int type) {
static std::ios_base::Init force_init;
std::cout << "registering: " << type;
return type;
}
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.