template trick?

J

John Carson

Jacek Dziedzic said:
Hello!

I have a templated class that serves as a simple vector of
elements.

template <typename T>
class simple_vector : public math_object {
// ... lots of simple_vector operations

// the trouble begins here:

T sum() {
// calculates and returns a sum of all elements of the vector
}
};

The trouble begins whenever I try to have a vector that would
hold elements of, say, double[6]. Trying to instantiate that
gives an error, because the return type of sum() becomes an
array, which is impossible.

Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?

I was hoping that specialization would fix this, but whenever
I try to move the sum() function out of the template declaration
and make it a

template<> simple_vector<int>::sum() {
}

the compiler complains that the template does not contain a
function called sum(). But of course leaving a declaration of
sum as T sum() within the template declaration will lead to
the same error that sum() can't return an array.

I've googled c.l.c++ only to begin to suspect that I need function
specialization and not template specialization and that it cannot
be done. Is it so?

You can return a pointer or reference to an array if a pointer or reference
would be acceptable for all types. Alternatively, you could make sum() a
friend of the class rather than a member.

Finally, you could specialise the entire class rather than attempting to
specialise just a member function. In that case you must declare and define
the entire class again for the particular type you are interested in. To do
this, you first declare the general class. Then you declare the specialised
version, which may but need not have anything in common with the general
case, e.g.,

#include <iostream>

template <typename T>
class simple_vector
{
T t1, t2;
public:
void print() { std::cout << t1 << '\n'; }
T sum()
{
return t1+t2;
}
};


template <>
class simple_vector<double[6]>
{
double d[6];
public:
simple_vector()
{
for (int i=0; i<6; ++i)
d = i*i;
}
void print()
{
for (int i=0; i<6; ++i)
std::cout << d<< '\n';
}
};


int main()
{
simple_vector<double[6]> v;
v.print();
return 0;
}
 
L

Leor Zolman

Hello!

I have a templated class that serves as a simple vector of
elements.

template <typename T>
class simple_vector : public math_object {
// ... lots of simple_vector operations

// the trouble begins here:

T sum() {
// calculates and returns a sum of all elements of the vector
}
};

The trouble begins whenever I try to have a vector that would
hold elements of, say, double[6]. Trying to instantiate that
gives an error, because the return type of sum() becomes an
array, which is impossible.

I think you're SOL with respect to elements of type double[6], because
they're not assignable or copyable. You can't put things into containers
that aren't assignable/copyable. I feel like an idiot, because I've spent
an hour or more playing with this, until I finally got an error that made
me realize there's no way it can ever work.

Perhaps you can make do with storing vectors instead of the arrays of 6
doubles?
-leor

Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?

I was hoping that specialization would fix this, but whenever
I try to move the sum() function out of the template declaration
and make it a

template<> simple_vector<int>::sum() {
}

the compiler complains that the template does not contain a
function called sum(). But of course leaving a declaration of
sum as T sum() within the template declaration will lead to
the same error that sum() can't return an array.

I've googled c.l.c++ only to begin to suspect that I need function
specialization and not template specialization and that it cannot
be done. Is it so?

NB: this is only a stripped-down example. Suggestions to use
a std::vector<double> instead of double[6] or to create a double6
struct won't fix my problem.

So in general: what I need is a template, a few functions of
which need to look differently for different T's, where T is the
template typename parameter. Or better -- can I make some
template member function hidden for certain T's, for which
these functions don't make sense/become illegal?

thanks in advance,
- J.
 
J

Jacek Dziedzic

Hello!

I have a templated class that serves as a simple vector of
elements.

template <typename T>
class simple_vector : public math_object {
// ... lots of simple_vector operations

// the trouble begins here:

T sum() {
// calculates and returns a sum of all elements of the vector
}
};

The trouble begins whenever I try to have a vector that would
hold elements of, say, double[6]. Trying to instantiate that
gives an error, because the return type of sum() becomes an
array, which is impossible.

Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?

I was hoping that specialization would fix this, but whenever
I try to move the sum() function out of the template declaration
and make it a

template<> simple_vector<int>::sum() {
}

the compiler complains that the template does not contain a
function called sum(). But of course leaving a declaration of
sum as T sum() within the template declaration will lead to
the same error that sum() can't return an array.

I've googled c.l.c++ only to begin to suspect that I need function
specialization and not template specialization and that it cannot
be done. Is it so?

NB: this is only a stripped-down example. Suggestions to use
a std::vector<double> instead of double[6] or to create a double6
struct won't fix my problem.

So in general: what I need is a template, a few functions of
which need to look differently for different T's, where T is the
template typename parameter. Or better -- can I make some
template member function hidden for certain T's, for which
these functions don't make sense/become illegal?

thanks in advance,
- J.
 
J

John Harrison

Leor Zolman said:
Hello!

I have a templated class that serves as a simple vector of
elements.

template <typename T>
class simple_vector : public math_object {
// ... lots of simple_vector operations

// the trouble begins here:

T sum() {
// calculates and returns a sum of all elements of the vector
}
};

The trouble begins whenever I try to have a vector that would
hold elements of, say, double[6]. Trying to instantiate that
gives an error, because the return type of sum() becomes an
array, which is impossible.

I think you're SOL with respect to elements of type double[6], because
they're not assignable or copyable. You can't put things into containers
that aren't assignable/copyable. I feel like an idiot, because I've spent
an hour or more playing with this, until I finally got an error that made
me realize there's no way it can ever work.

Perhaps you can make do with storing vectors instead of the arrays of 6
doubles?
-leor

boost have a fixed size array class, specifically design to be usable in the
STL. Presumably it would be usable with the OPs code as well.

http://www.boost.org/doc/html/array.html

john
 
L

Leor Zolman

boost have a fixed size array class, specifically design to be usable in the
STL. Presumably it would be usable with the OPs code as well.

http://www.boost.org/doc/html/array.html

Sure, I think any true objects (that satisfy the container requirements)
would be fine. The thing that was deceptive about trying to store naked
arrays is that you can get "just so far", before you actually try to do
anything useful, and it compiles. The trouble first showed up for me, in
the case of the OP's example, when I attempted to create constructors for
his container class...if there's only a default ctor, then you'd have to
add elements using push_back or insert... but you /can't/ because you can't
pass an array to a function:
simple_vector<double[6]> v; // OK
double d[6] = {...};
v.push_back(d); // Bzzzzzt! You lose!

So my next clever idea was to create a ctor that takes a size, in order to
"pre-allocate" the arrays:

simple_vector<double[6]> v(5); // Bzzzt!

then just go in and fill in the values one at a time with assignment
statements a la:
v[0][0] = .2;
v[0][1] = .5; // etc.

The assignments would work (I guess, although I never got to the point of
being able to find out...), but the constructor can't--because it would
need to be able to default initialize each double[6], which of course can't
be done. So the entire house of cards comes tumbling down.

I figure you (John) understands this, I was just spelling it out for the
benefit of anyone else (like me) who actually tried to make this work as
advertised. I doubt I'll make the same mistake again ;-)
-leor
 
J

Jacek Dziedzic

John said:
You can return a pointer or reference to an array if a pointer or reference
would be acceptable for all types.

No, I can't afford the performance loss. My simple_vector class
serves as an envelope for traditional arrays of
doubles/ints/double[6]'s I get from somewhere else and for my
own arrays that I'd like to be handled in a clean manner.
Performance, however, is critical to me (this is a part of a scientific
computation)
Alternatively, you could make sum() a
friend of the class rather than a member.

Finally, you could specialise the entire class rather than attempting to
specialise just a member function. In that case you must declare and define
the entire class again for the particular type you are interested in. To do
this, you first declare the general class. Then you declare the specialised
version, which may but need not have anything in common with the general
case, e.g.,

#include <iostream>

template <typename T>
class simple_vector
{
T t1, t2;
public:
void print() { std::cout << t1 << '\n'; }
T sum()
{
return t1+t2;
}
};


template <>
class simple_vector<double[6]>
{
double d[6];
public:
simple_vector()
{
for (int i=0; i<6; ++i)
d = i*i;
}
void print()
{
for (int i=0; i<6; ++i)
std::cout << d<< '\n';
}
};


int main()
{
simple_vector<double[6]> v;
v.print();
return 0;
}


The trouble is, I'd have to repeat 30KB of source only to change
a single line in the second occurrence...

- J.
 
J

Jacek Dziedzic

John said:
I think you're SOL with respect to elements of type double[6], because
they're not assignable or copyable. You can't put things into containers
that aren't assignable/copyable. I feel like an idiot, because I've spent
an hour or more playing with this, until I finally got an error that made
me realize there's no way it can ever work.

My answer to Leor here (my newsserver is still allergic to your
posts, so I can only see replies to your replies)...

If you can't put things that aren't assignable copyable into
containers, then I guess my simple_vector class is not a container.
Within the realm of my class I don't need the 'thing' to be
copyable or assignable.

I realize sum() would never work for double[6], because you
can't return them. The thing is I want sum() to work for some
chosen types (like int and double) *only*. I don't care if
it's meaningless to call simple_vector<double[6]>::sum(),
if only it would compile... In other words I'd like this
to work:

double s;
simple_vector<double> v(10);
s=v.sum()

and this to work

simple_vector<double[6]> v(10);
// note: no usage of sum()

and this to either not compile or produce a runtime error:


Unfortunately not. The 'thing' needs to be POD, as it is
raw-memory-copied between processors on a parallel system.
Also I can't afford the memory overhead of std::vector
(these simple_vectors run into GB's of memory).

I'd rather make double[6] into a SixDoubles class or
struct if no other solution emerges.
boost have a fixed size array class, specifically design to be usable in the
STL. Presumably it would be usable with the OPs code as well.

You mean instead of the double[6] or the whole simple_vector?
If the latter, no way. This simple_vector has very specific
functionality that I'm almost sure no 3rd party implementation
provides. If the former, then I'd rather make that double[6] into
a struct.

- J.
 
L

Leor Zolman

John said:
I think you're SOL with respect to elements of type double[6], because
they're not assignable or copyable. You can't put things into containers
that aren't assignable/copyable. I feel like an idiot, because I've spent
an hour or more playing with this, until I finally got an error that made
me realize there's no way it can ever work.

My answer to Leor here (my newsserver is still allergic to your
posts, so I can only see replies to your replies)...

Darn. I'm sending you an email copy of this post, then. I don't know why
your news server hates me, but if you ever figure it out, please let me
know if there's anything I can do about it. I just use Agent, and I post
from comcast.net...
If you can't put things that aren't assignable copyable into
containers, then I guess my simple_vector class is not a container.
Within the realm of my class I don't need the 'thing' to be
copyable or assignable.

Whoops, my own confusion there. In the code I was playing around with, I
had simple_vector inherit from std::vector, and then I somehow got that
turned around in my head to where I thought the idea came from your code.
Now I see that it didn't. Sorry!

I may not have enough time to come up with an answer before someone else
gives you a better one (my newsgroup window for today is about to close),
but I thought I'd at least explain why my previous posts seemed so out in
left field.
-leor
 
A

Alf P. Steinbach

* Jacek Dziedzic said:
Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?

#include <vector>
#include <iostream>

struct DoubleArray6
{
double elem[6];
};

int main()
{
static DoubleArray6 const x = {10, 20, 30, 40, 50, 60};

std::vector<DoubleArray6> v;
v.push_back( x );
for( size_t i = 0; i < 6; ++i )
{
std::cout << v[0].elem << std::endl;
}
}

Just use your own vector class instead of std::vector.
 
J

Jacek Dziedzic

Alf said:
* Jacek Dziedzic said:
Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?


#include <vector>
#include <iostream>

struct DoubleArray6
{
double elem[6];
};

int main()
{
static DoubleArray6 const x = {10, 20, 30, 40, 50, 60};

std::vector<DoubleArray6> v;
v.push_back( x );
for( size_t i = 0; i < 6; ++i )
{
std::cout << v[0].elem << std::endl;
}
}

Just use your own vector class instead of std::vector.


Yes, that was the sort-of-solution I came up with also.
What I don't like about it, however, is that I've already
done it with double[3], having now a simple Vect3d class,
now I'd have to add something like Vect6d. I'd go with
this for now, because time presses me, but I had really hoped
there was some solution more closer to the class itself
than to the user code using the class...

Or in other words... This solution is along the lines of
"Here's a simple_vector class template, it holds objects
of the type you desire, except for arrays. You can't store
arrays in it, unless you pack them in a separate type.
Alas, you'll need one extra type for each kind of array
you want, or you'll need to use a vector and face the
overhead."

I was hoping for a solution that would fix the template and
go like "Here'a simple_vector class template, it holds
objects of the type you desire, including arrays. Be warned
though that some methods of this templated class are of no
use on some types, so don't try to sum() arrays for example."

I must admit I had only a vague idea about template specialization
and actually thought that was a very easy task. I mean -- can't
you really have a template that works the same for all supported
types, save one, for which it has one function redefined? That
is, without having to write the 99% of the template twice?

Can someone help on the template-like solution?

TIA,
- J.
 
L

Leor Zolman

I realize sum() would never work for double[6], because you
can't return them. The thing is I want sum() to work for some
chosen types (like int and double) *only*. I don't care if
it's meaningless to call simple_vector<double[6]>::sum(),
if only it would compile... In other words I'd like this
to work:

double s;
simple_vector<double> v(10);
s=v.sum()

and this to work

simple_vector<double[6]> v(10);
// note: no usage of sum()

and this to either not compile or produce a runtime error:

simple_vector<double[6]> v(10);
v.sum();

Okay, all I can think of is to take the sum functionality out of the class
and make it into a non-member function. Here's an implementation where the
version of sum taking the simple_vector<double[6]> actually does something
useful:

#include <iostream>
using namespace std;

template <typename T>
class simple_vector {
public:
simple_vector(int i) : vsize(i), ptr(new T) {}
int size() const { return vsize; }
T &operator[](int index) { return ptr[index]; }
T const &operator[](int index) const { return ptr[index]; }
public:
T *ptr;
int vsize;
};


template<typename T>
T sum(simple_vector<T> s) {
// calculates and returns a sum of all elements of the vector
T result = 0;
for (int i = 0; i < s.size(); ++i)
result += s.ptr;
return result;
}

template<typename T, int N>
void sum(simple_vector<T[N]> sv, T *result)
{
for (int i = 0; i < N; ++i)
{
T total = 0;
for (int j = 0; j < sv.size(); j++)
total += sv[j];
result = total;
}
}


int main()
{
simple_vector<double> v(10);
double s;
s = sum(v);
cout << "sum(v) = " << s << endl;

simple_vector<double[2]> v2(2);
v2[0][0] = .2;
v2[0][1] = .2;
v2[1][0] = .3;
v2[1][1] = .3;
double result[2];
sum(v2, result);
cout << "result[0] = " << result[0] << endl;
cout << "result[1] = " << result[1] << endl;

return 0;
}


HTH,
-leor
 
B

bartek

Alf said:
* Jacek Dziedzic said:
Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?


#include <vector>
#include <iostream>

struct DoubleArray6
{
double elem[6];
};

int main()
{
static DoubleArray6 const x = {10, 20, 30, 40, 50, 60};

std::vector<DoubleArray6> v;
v.push_back( x );
for( size_t i = 0; i < 6; ++i )
{
std::cout << v[0].elem << std::endl;
}
}

Just use your own vector class instead of std::vector.


Yes, that was the sort-of-solution I came up with also.
What I don't like about it, however, is that I've already
done it with double[3], having now a simple Vect3d class,
now I'd have to add something like Vect6d. I'd go with
this for now, because time presses me, but I had really hoped
there was some solution more closer to the class itself
than to the user code using the class...

Or in other words... This solution is along the lines of
"Here's a simple_vector class template, it holds objects
of the type you desire, except for arrays. You can't store
arrays in it, unless you pack them in a separate type.
Alas, you'll need one extra type for each kind of array
you want, or you'll need to use a vector and face the
overhead."

I was hoping for a solution that would fix the template and
go like "Here'a simple_vector class template, it holds
objects of the type you desire, including arrays. Be warned
though that some methods of this templated class are of no
use on some types, so don't try to sum() arrays for example."

I must admit I had only a vague idea about template specialization
and actually thought that was a very easy task. I mean -- can't
you really have a template that works the same for all supported
types, save one, for which it has one function redefined? That
is, without having to write the 99% of the template twice?

Can someone help on the template-like solution?


You could specialise the simple_vector template for array types.
E.g.

template <class T>
class simple_vector {
T sum() { ... }
};

template <class T, int n>
class simple_vector<T[n]> {
T* sum() { ... }
};

Though, not all compilers support such specialisations, unfortunately.

Cheers!
 
B

bartek

(...)
You could specialise the simple_vector template for array types.
E.g.

template <class T>
class simple_vector {
T sum() { ... }
};

template <class T, int n>
class simple_vector<T[n]> {
T* sum() { ... }
};

Though, not all compilers support such specialisations, unfortunately.

OK, please forget it. sum() returning T* is stupid... stupid me...
 
J

John Carson

You didn't comment on this. Performances issues again? (I wouldn't know.)
The trouble is, I'd have to repeat 30KB of source only to change
a single line in the second occurrence...

I am not certain of this, but I think that the repetition only makes a
difference to your source code, not your binary. If you instantiate a single
template class for, say, int and double[6], then what you get is effectively
two classes, one for int and one for double[6]. The basic difference is just
that the compiler has written them rather than you. The template itself
should not go into the source code, just the instantiations of it.
Accordingly, it shouldn't make a difference to the size of the binary
whether both instantiations use the same template or each draws on its own
template. Of course, it is less elegant from a coding standpoint to have to
repeat an entire template class.

In any event, a better solution is to template the return type of the sum
function and then provide a specialisation of the sum function for
double[6]. Note that the second template parameter defaults to the first
parameter.

template <typename T, typename R=T>
class simple_vector
{
T t1,t2;
public:
R sum()
{
return t1+t2;
}
};

template <>
int simple_vector<double[6], int>::sum()
{
return 0;
}

You declare an instance of the class for an int with:

simple_vector<int> v1;

More generally, for any type X for which the default sum function makes
sense, you declare an instance with

simple_vector<X> v1;

By contrast, you declare an instance of the class for double[6] with:

simple_vector<double[6], int> v2;
 
J

John Carson

In any event, a better solution is to template the return type of the
sum function and then provide a specialisation of the sum function for
double[6]. Note that the second template parameter defaults to the
first parameter.

template <typename T, typename R=T>
class simple_vector
{
T t1,t2;
public:
R sum()
{
return t1+t2;
}
};

template <>
int simple_vector<double[6], int>::sum()
{
return 0;
}

You declare an instance of the class for an int with:

simple_vector<int> v1;

More generally, for any type X for which the default sum function
makes sense, you declare an instance with

simple_vector<X> v1;

By contrast, you declare an instance of the class for double[6] with:

simple_vector<double[6], int> v2;


Better still is to make the specialisation with R==void rather than R==int.
When T==double[6], calling sum() will compile but it will not compile if you
assign the return value to anything.

template <typename T, typename R=T>
class simple_vector
{
T t1,t2;
public:
R sum()
{
return t1+t2;
}
};

template <>
void simple_vector<double[6], void>::sum()
{}


simple_vector<int> v1;
simple_vector<double[6], void> v2;

int main()
{
v1.sum(); // compiles
v2.sum(); // compiles
double d = v2.sum(); // won't compile
return 0;
}
 
L

Leor Zolman

In any event, a better solution is to template the return type of the
sum function and then provide a specialisation of the sum function for
double[6]. Note that the second template parameter defaults to the
first parameter.

template <typename T, typename R=T>
class simple_vector
{
T t1,t2;
public:
R sum()
{
return t1+t2;
}
};

template <>
int simple_vector<double[6], int>::sum()
{
return 0;
}

Nice! Having the 2nd template parameter default to the first is gorgeous.
Wish I'd thought of that ;-)
-leor
 
A

Andrey Tarasevich

Jacek said:
...
I have a templated class that serves as a simple vector of
elements.

template <typename T>
class simple_vector : public math_object {
// ... lots of simple_vector operations

// the trouble begins here:

T sum() {
// calculates and returns a sum of all elements of the vector
}
};

The trouble begins whenever I try to have a vector that would
hold elements of, say, double[6]. Trying to instantiate that
gives an error, because the return type of sum() becomes an
array, which is impossible.

Is there a simple way to let the user have instances of
simple_vector<int> for which the sum() operation makes sense
while also allowing simple_vector<double[6]> to be instantiated
and only break upon trying to sum() these?
...

You can probably use some traits-based technique. If you don't care for
a useable 'sum' in 'simple_vector<double[6]>', it can be done, for
example, as follows

template <typename T_> struct type_traits {
typedef T_ return_t;
};

template <> struct type_traits<double[6]> {
typedef void return_t;
};

template <typename T_>
class simple_vector : public math_object {
...
typename type_traits<T_>::return_t sum();
...
};

(Partial specialization can be used to cover all arrays, not just
'double[6]' as in the above example).
 
A

Andrey Tarasevich

Andrey said:
You can probably use some traits-based technique. If you don't care for
a useable 'sum' in 'simple_vector<double[6]>', it can be done, for
example, as follows
...

I just noticed that John Carson already suggested a similar approach.
 

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

Forum statistics

Threads
474,169
Messages
2,570,918
Members
47,458
Latest member
Chris#

Latest Threads

Top