On Wed, 4 Jan 2017 17:18:06 -0500
bitrex <
bit...@de.lete.earthlink.net> wrote:
> The following snippet compiles with no warnings under GCC on Compiler
> Explorer, while Clang warns on the line "init() : _ref(new (_buf)
> T())" that "warning: instantiation of variable 'Bar<Foo>::_buf'
> required here, but no definition is available
> [-Wundefined-var-template]"
>
> When attempting to run the code on Ideone it fails with a linker
> error about an undefined reference on the same line.
>
> Is there a way to make a hack like this work "properly", i.e. have
> get_ref() return a reference a templated class where "placement new"
> has been used to instantiate the class on a static buffer, without
> resorting to initializing the static field for the buffer outside the
> class definition?
>
> I would have thought that since _buf had static storage duration that
> I could use placement new to initialize the const-qualified pointer
> within the initializer list, even though _buf is instantiated within
> the constructor. I imagine there is something somewhere in the
> standard which says I am wrong.
If is ODR-used, then it must be defined. In addition, in your usage it
also needs to be initialized before first use. See below.
> #include <cstddef>
> #include <iostream>
> #include <new>
>
> struct Foo {
> Foo() = default;
> void print_it() {
> std::cout << "hey hey hi" << std::endl;
> }
> };
>
> template <typename T>
> class Bar
> {
> public:
> Bar() = default;
> ~Bar() = default;
>
> void print_it()
> {
> get_ref().print_it();
> }
>
> private:
> static char* _buf;
>
> static T& get_ref() {
> struct init {
> public:
> init() : _ref(new (_buf) T()) {
> _buf = new char[sizeof(T)];
> }
The first time you call get_ref(), you use placement new in the member
initializer list before the block of memory pointed to by 'buf' has
been allocated in the constructor. Surely you get a segfault with gcc
(which you say makes the mistake of trying to compile and link it)?
(Apart from that, the code cannot possibly serve any useful purpose
because every time print_it() is called after that first time, a 'T'
object will be constructed in the formerly allocated memory, but then
immediately be replaced by reallocated memory in the constructor
which will have no object constructed in it. In consequence, you have
a memory leak, and you also have a second round of undefined behavior by
the call to the Foo::print_it() non-static method on a Foo object which
does not exist.)