#! /bin/bash # When I run this on Ubuntu-x86_64, Macintosh(clang) or a Raspberry pi # the code links and when it runs it just prints 999 - which is the behaviour # that I expect. On both Cygwin and using x86_64-w64-mingw32-g++ (and i686) # I get a linker diagnostic of the form # ./tltest.sh # /usr/lib/gcc/x86_64-pc-cygwin/8.3.0/../../../../x86_64-pc-cygwin/bin/ld: # /tmp/cczVTcZ1.o:t2.cpp:(.text+0x86): multiple definition of # `TLS init function for Data::valref'; # /tmp/ccyLQlRb.o:t1.cpp:(.text+0x4a): first defined here # collect2: error: ld returned 1 exit status # # I believe that given the specification of C++17 "inline" variables this # is incorrect, but there are better experts who may be able to explain # otherwise. # # When people raise issues here I often see other asking "Why are you doing # that?". This is a cut-down version of my real code where rather than # storing a reference to the thread_local Record that I am interested in # in a simple array (tvec) at a fixed offset (1) I store and retrieve a # referece to it using TlsAlloc(), TlsGetValue and TlsSetValue - those being # the Microsoft API for thread-local access, and for my purposes my # measurements suggest that using them gives me useful performance gains # over the emutls code that g++ creates on the relevant platforms. # And I am then (trying to) build a header-only library (for which t.h is # the surrogate here) which can be included from several other compilation # units but by virtue of "inline variables" its private (including thread- # local) date can be defined within that header file so that the files that # #include the header-only library do not need to contain anything beyond # uses of it. To illustrate this I have two source files which each include # t.h but the bad behaviour does not need any other code in the first one! # The second just contains a tiny main program that inspects data from the # library data. # Have I misunderstood C++ and so am I doing something wrong or is this # a g++/Windows bug? cat < t.h #include inline void *tvec[4]; class Record { public: int val = 999; }; class Data_Ref { static inline thread_local Record val; public: static Record* get() // Get reference to Record via tvec[] { return (Record*)tvec[1]; } Data_Ref() // Stash it in tvec[] for later use { tvec[1] = (void *)&val; } }; class Data { static inline thread_local Data_Ref valref; public: static Record &get() { return *valref.get(); // note that get() is static in Data_Ref } }; XXX cat < t1.cpp #include #include "t.h" // First copy XXX cat < t2.cpp #include #include "t.h" // Second copy int main() { std::cout << Data::get().val << std::endl; return 0; } XXX ${1:-g++} -std=c++17 -I. t1.cpp t2.cpp -o t ./t # end of test script