Valid uses of template parameters

D

Dave Theese

Please see questions in the code below... Thanks!

#include <iostream>

using namespace std;

class foo_class
{
public:
foo_class() {cout << "Constructing..." << endl;}
~foo_class() {cout << "Destructing..." << endl;}
};

template <typename T>
void foo()
{
unsigned char mem[100];
T *ptr;

ptr = new(mem) T;

ptr->~T(); // Funky destructor call - "concatenating" '~' with T's value.
// In an overly-simplified sense, templates are a text
// substitution mechanism, but this use of a template
parameter is
// different than what is usually seen. Is this really legal?
}

int main()
{
foo<foo_class>(); // Outputs "Constructing...\nDestructing...\n"
// Is my compiler exhibiting correct behavior? Should
this
// be legal (given what happens inside foo())?

foo<int>(); // This works too! How is that happening? What does it mean
to
// "destruct" an int (which is what happens inside foo())???

return 0;
}
 
D

David B. Held

Dave Theese said:
[...]
template <typename T>
void foo()
{
unsigned char mem[100];
T *ptr;

ptr = new(mem) T;

You'd better hope sizeof(T) <= 100, or else you should do
this instead:

template <typename T, int N = sizeof(T)>
void foo()
{
unsigned char mem[N];
// ...
}
ptr->~T(); // Funky destructor call - "concatenating" '~'
// with T's value. In an overly-simplified
// sense, templates are a text substitution
// mechanism, but this use of a template
// parameter is different than what is usually
// seen. Is this really legal?

Yes. That's the only way to explicitly name the d'tor when
T is a parameterized type.
}

int main()
{
foo<foo_class>(); // Outputs "Constructing...\nDestructing...\n"
// Is my compiler exhibiting correct behavior?
// Should this be legal (given what happens
// inside foo())?

As far as I know that is the expected behaviour. However,
there is the technicality that mem in foo() is not guaranteed
to be correctly aligned for T. That may lead to undefined
behaviour.
foo<int>(); // This works too! How is that happening?
// What does it mean to "destruct" an int (which
// is what happens inside foo())???

return 0;
}

Well, one would expect that the d'tor for POD types would
be trivial, so it most probably means "do nothing". And
the reason you are able to call ~T() for [T = int] is precisely
to aid generic programming. Note that you can't call
~int().

Dave
 
D

Dave Theese

David B. Held said:
Dave Theese said:
[...]
template <typename T>
void foo()
{
unsigned char mem[100];
T *ptr;

ptr = new(mem) T;

You'd better hope sizeof(T) <= 100, or else you should do
this instead:

template <typename T, int N = sizeof(T)>
void foo()
{
unsigned char mem[N];
// ...
}
ptr->~T(); // Funky destructor call - "concatenating" '~'
// with T's value. In an overly-simplified
// sense, templates are a text substitution
// mechanism, but this use of a template
// parameter is different than what is usually
// seen. Is this really legal?

Yes. That's the only way to explicitly name the d'tor when
T is a parameterized type.
}

int main()
{
foo<foo_class>(); // Outputs "Constructing...\nDestructing...\n"
// Is my compiler exhibiting correct behavior?
// Should this be legal (given what happens
// inside foo())?

As far as I know that is the expected behaviour. However,
there is the technicality that mem in foo() is not guaranteed
to be correctly aligned for T. That may lead to undefined
behaviour.
foo<int>(); // This works too! How is that happening?
// What does it mean to "destruct" an int (which
// is what happens inside foo())???

return 0;
}

Well, one would expect that the d'tor for POD types would
be trivial, so it most probably means "do nothing". And
the reason you are able to call ~T() for [T = int] is precisely
to aid generic programming. Note that you can't call
~int().

Dave

Yep, the alignment issues that you and Kevin raised are, of course, valid.
I was just trying to make the example as simplistic as possible to try and
focus attention on the ~type() issue. Well, this is all quite interesting.
I suppose it's somewhat analagous to T() being well-defined, even for
built-in types. Thank you both!!!
 
D

Dave Theese

David B. Held said:
Dave Theese said:
[...]
template <typename T>
void foo()
{
unsigned char mem[100];
T *ptr;

ptr = new(mem) T;

You'd better hope sizeof(T) <= 100, or else you should do
this instead:

template <typename T, int N = sizeof(T)>
void foo()
{
unsigned char mem[N];
// ...
}
ptr->~T(); // Funky destructor call - "concatenating" '~'
// with T's value. In an overly-simplified
// sense, templates are a text substitution
// mechanism, but this use of a template
// parameter is different than what is usually
// seen. Is this really legal?

Yes. That's the only way to explicitly name the d'tor when
T is a parameterized type.
}

int main()
{
foo<foo_class>(); // Outputs "Constructing...\nDestructing...\n"
// Is my compiler exhibiting correct behavior?
// Should this be legal (given what happens
// inside foo())?

As far as I know that is the expected behaviour. However,
there is the technicality that mem in foo() is not guaranteed
to be correctly aligned for T. That may lead to undefined
behaviour.
foo<int>(); // This works too! How is that happening?
// What does it mean to "destruct" an int (which
// is what happens inside foo())???

return 0;
}

Well, one would expect that the d'tor for POD types would
be trivial, so it most probably means "do nothing". And
the reason you are able to call ~T() for [T = int] is precisely
to aid generic programming. Note that you can't call
~int().

Dave

On looking a little closer, the proposal of a function template default
argument value won't work, as such defaults are allowed only in class
tempaltes. But the point is well-taken!
 
D

Dave Theese

David B. Held said:
Dave Theese said:
[...]
template <typename T>
void foo()
{
unsigned char mem[100];
T *ptr;

ptr = new(mem) T;

You'd better hope sizeof(T) <= 100, or else you should do
this instead:

template <typename T, int N = sizeof(T)>
void foo()
{
unsigned char mem[N];
// ...
}
ptr->~T(); // Funky destructor call - "concatenating" '~'
// with T's value. In an overly-simplified
// sense, templates are a text substitution
// mechanism, but this use of a template
// parameter is different than what is usually
// seen. Is this really legal?

Yes. That's the only way to explicitly name the d'tor when
T is a parameterized type.
}

int main()
{
foo<foo_class>(); // Outputs "Constructing...\nDestructing...\n"
// Is my compiler exhibiting correct behavior?
// Should this be legal (given what happens
// inside foo())?

As far as I know that is the expected behaviour. However,
there is the technicality that mem in foo() is not guaranteed
to be correctly aligned for T. That may lead to undefined
behaviour.
foo<int>(); // This works too! How is that happening?
// What does it mean to "destruct" an int (which
// is what happens inside foo())???

return 0;
}

Well, one would expect that the d'tor for POD types would
be trivial, so it most probably means "do nothing". And
the reason you are able to call ~T() for [T = int] is precisely
to aid generic programming. Note that you can't call
~int().

Dave

And as long as we're at it, is there concurrence that the following will
eliminate the alignment / size issues raised by Kevin and Dave about my
original example? I plan on archiving this example, so I should get it
right in all respects...


#include <iostream>

using namespace std;

class foo_class
{
public:
foo_class() {cout << "Constructing..." << endl;}
~foo_class() {cout << "Destructing..." << endl;}
};

template <typename T>
void foo()
{
void *mem;
T *ptr;

mem = operator new(sizeof(T));
ptr = new(mem) T;
ptr->~T();
operator delete(mem);
}

int main()
{
foo<foo_class>();
foo<int>();

return 0;
}
 
K

Kevin Goodsell

Dave Theese wrote:

(In the future please trim when replying. We don't need 500-lines of
irrelevant text tagged onto a reply.)
And as long as we're at it, is there concurrence that the following will
eliminate the alignment / size issues raised by Kevin and Dave about my
original example? I plan on archiving this example, so I should get it
right in all respects...


#include <iostream>

using namespace std;

class foo_class
{
public:
foo_class() {cout << "Constructing..." << endl;}
~foo_class() {cout << "Destructing..." << endl;}
};

template <typename T>
void foo()
{
void *mem;
T *ptr;

mem = operator new(sizeof(T));

I strongly suspect that this will solve the alignment problem (in
practice), but I'm not 100% sure. I'm even less sure that the standard
requires it to work. I only know of one method that I am completely sure
will work, and that is to use malloc() or calloc() to allocate the memory.

There are other methods that are likely to work (the most common being
to define a union object containing several different types of objects,
the goal being to force it to have the strictest possible alignment
requirements), but they are considerably more complicated.
ptr = new(mem) T;
ptr->~T();
operator delete(mem);
}

int main()
{
foo<foo_class>();
foo<int>();

return 0;
}

-Kevin
 
D

David B. Held

Dave Theese said:
[...]
And as long as we're at it, is there concurrence that the
following will eliminate the alignment / size issues raised
by Kevin and Dave about my original example? I plan on
archiving this example, so I should get it right in all
respects...
[...]
template <typename T>
void foo()
{
void *mem;
T *ptr;

mem = operator new(sizeof(T));
ptr = new(mem) T;
ptr->~T();
operator delete(mem);
}
[...]

I know that an array of char returned from new is
guaranteed to have proper alignment for any type
that is the size of the array. I'm not sure if an
operator new call provides the same guarantee or
not, because my copy of the standard is not handy
right now, and I'm too lazy to look it up. I would
assume that memory returned from operator new
would have suitable alignment, or else it wouldn't
be very useful.

Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,141
Messages
2,570,817
Members
47,364
Latest member
Stevanida

Latest Threads

Top