Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Static member function initialization and placement new

18 views
Skip to first unread message

bitrex

unread,
Jan 4, 2017, 5:18:28 PM1/4/17
to
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.

#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)];
}

operator T*() const {return _ref; }

private:
T *const _ref;
};

static init _init;
return *_init;
}
};

int main ()
{
auto b = Bar<Foo>();
b.print_it();
return 0;
}

Ian Collins

unread,
Jan 4, 2017, 5:28:07 PM1/4/17
to
On 01/ 5/17 11:18 AM, bitrex 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 don't think there is, Bar<T>::_buf still has to be defined, whether
placement new or regular new is used.

Don't forget that static data members aren't part of class instances.

--
Ian

Chris Vine

unread,
Jan 4, 2017, 8:13:36 PM1/4/17
to
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.)

bitrex

unread,
Jan 5, 2017, 1:41:54 AM1/5/17
to
On 01/04/2017 08:13 PM, Chris Vine wrote:

> 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)?

It does appear to compile under later versions of GCC on Compiler
Explorer with no errors, and even produces an assembly output:

https://godbolt.org/g/u8yPUZ

Clang produces a warning about the issue you mention.

On my home PC using GCC 5.4 it fails with a linker error.

> (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.)

Why would the the "T" object be constructed repeatedly? In the "get_ref"
function the struct "init" is only instantiated once and is declared
with static storage duration.

This compiles under GCC and Clang with no errors or warnings, runs OK
and seems to be what I was getting at:

#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 *const buf_init()
{
static char* _buf = new char[sizeof(T)];
return _buf;
}

static T& get_ref()
{
struct init
{
public:
init() : _ref(new (buf_init()) T())
{
std::cout << "I was only called once." << std::endl;
}

operator T*() const
{
return _ref;
}

private:
T *const _ref;
};

static init _init;
return *_init;
}
};

int main ()
{
auto b = Bar<Foo>();
for (size_t i = 0; i < 10; ++i) { b.print_it(); }
return 0;
}

Chris Vine

unread,
Jan 5, 2017, 6:10:05 AM1/5/17
to
On Thu, 5 Jan 2017 01:41:46 -0500
bitrex <bit...@de.lete.earthlink.net> wrote:
> On 01/04/2017 08:13 PM, Chris Vine wrote:
>
> > 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)?
>
> It does appear to compile under later versions of GCC on Compiler
> Explorer with no errors, and even produces an assembly output:
>
> https://godbolt.org/g/u8yPUZ
>
> Clang produces a warning about the issue you mention.
>
> On my home PC using GCC 5.4 it fails with a linker error.
>
> > (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.)
>
> Why would the the "T" object be constructed repeatedly? In the
> "get_ref" function the struct "init" is only instantiated once and is
> declared with static storage duration.

Yes, you are right about that.
Yes that looks OK. Function statics avoid both the issues that you ran
into (the absence of a definition and use before initialization).

0 new messages