problem on function template

S

shuisheng

Dear All,

Assume I have two templates as follows:

//! Add #1: p3 = p1 + p2.
template<class _T1, class _T2, class _T3>
void Add(const _T1 *p1, const _T2 *p2, _T3 *p3, size_t n)
{
for (size_t i = 0; i < n; ++i) p3 = p1 + p2[2];
}

//! Add #2: p3 = p1 + d2.
template<class _T1, class _T2, class _T3>
void Add(const _T1 *p1, const _T2 d2, _T3 *p3, size_t n)
{
for (size_t i = 0; i < n; ++i) p3 = p1 + d2;
}

Where p1, p2, p3 are pointers pointing to a group of data, and d2 is
just a single data.

I test the following case:

//! A test.
int *p1 = new int [3]; p1[0] = 1; p1[0] = 2; p1[0] = 3;
int *p2 = new int [3]; p2[0] = 1; p2[0] = 2; p2[0] = 3;
int *p3 = new int [3];
Add(p1, p2, p3, 3);

And I find that the "Add(p1, p2, p3, 3)" tries to call the second
template and givea a compilor error like can not convert int* to int.
In fact the first template can also allow the calling and it will give
the right results. I can change the seconde template name to "Add2" to
get it right. Is there any better way to accomplish it.

Thanks,

Shuisheng
 
V

Victor Bazarov

shuisheng said:
Assume I have two templates as follows:

//! Add #1: p3 = p1 + p2.
template<class _T1, class _T2, class _T3>

It has been already suggested, drop the leading underscores. With
them your code is illegal and difficult to read.
void Add(const _T1 *p1, const _T2 *p2, _T3 *p3, size_t n)
{
for (size_t i = 0; i < n; ++i) p3 = p1 + p2[2];
}

//! Add #2: p3 = p1 + d2.
template<class _T1, class _T2, class _T3>
void Add(const _T1 *p1, const _T2 d2, _T3 *p3, size_t n)
{
for (size_t i = 0; i < n; ++i) p3 = p1 + d2;
}

Where p1, p2, p3 are pointers pointing to a group of data, and d2 is
just a single data.

I test the following case:

//! A test.
int *p1 = new int [3]; p1[0] = 1; p1[0] = 2; p1[0] = 3;
int *p2 = new int [3]; p2[0] = 1; p2[0] = 2; p2[0] = 3;
int *p3 = new int [3];
Add(p1, p2, p3, 3);

And I find that the "Add(p1, p2, p3, 3)" tries to call the second
template and givea a compilor error like can not convert int* to int.


Correct. The compiler figures that T2 is "pointer to int" (the top-level
'const' qualifier is dropped), and that matches the type of 'p2' better
than 'const T2*' (with T2 'int') because the latter would require
a qualification conversion.
In fact the first template can also allow the calling and it will give
the right results. I can change the seconde template name to "Add2" to
get it right. Is there any better way to accomplish it.

Why are your 'Tx' types all different? Do they have to be? Couldn't you
simply have a single template argument:

template<class T> void Add(const T* p1, const T* p2, T* p3, size_t n)

and

template<class T> void Add(const T* p1, T d2, T* p3, size_t n)

? That would resolve the "ambiguity".

V
 
A

amparikh

shuisheng said:
Dear All,

Assume I have two templates as follows:

//! Add #1: p3 = p1 + p2.
template<class _T1, class _T2, class _T3>
void Add(const _T1 *p1, const _T2 *p2, _T3 *p3, size_t n)
{
for (size_t i = 0; i < n; ++i) p3 = p1 + p2[2];
}

//! Add #2: p3 = p1 + d2.
template<class _T1, class _T2, class _T3>
void Add(const _T1 *p1, const _T2 d2, _T3 *p3, size_t n)
{
for (size_t i = 0; i < n; ++i) p3 = p1 + d2;
}

Where p1, p2, p3 are pointers pointing to a group of data, and d2 is
just a single data.

I test the following case:

//! A test.
int *p1 = new int [3]; p1[0] = 1; p1[0] = 2; p1[0] = 3;
int *p2 = new int [3]; p2[0] = 1; p2[0] = 2; p2[0] = 3;
int *p3 = new int [3];
Add(p1, p2, p3, 3);

And I find that the "Add(p1, p2, p3, 3)" tries to call the second
template and givea a compilor error like can not convert int* to int.
In fact the first template can also allow the calling and it will give
the right results. I can change the seconde template name to "Add2" to
get it right. Is there any better way to accomplish it.

Thanks,


That's because the second function is more specialized than the first
function and the partial ordering rules come into the picture.
Basically in the first one T2 is deduced to be an int
and in the second case T2 is deduced to be an int*...

and if you use substitution rules governed by the partial ordering
mechanism, the second one is more specialized.
 
S

shuisheng

Victor Bazarov 写é“:
Why are your 'Tx' types all different? Do they have to be? Couldn't you
simply have a single template argument:

template<class T> void Add(const T* p1, const T* p2, T* p3, size_t n)

and

template<class T> void Add(const T* p1, T d2, T* p3, size_t n)

? That would resolve the "ambiguity".

That is because we mix-used the float and double type in our team
project.
 
V

Victor Bazarov

shuisheng said:
Victor Bazarov ??:


That is because we mix-used the float and double type in our team
project.

Ah.... I see. I was afraid you might say something like that :)

One solution is to overload your function to make the second 'Add'
take 'double' as the second argument:
~~~~~~~~~~~~~~~~~
template<class T1, class T3> void Add(const T1* p1, double d2,
const T3* p3, size_t n);
~~~~~~~~~~~~~~~~~

Another solution is to drop 'const' from the arguments, and rely on
the proper argument deduction by the compiler:
~~~~~~~~~~~~~~~~~
#include <stdlib.h>

template<class T1, class T2, class T3>
void add(T1* p1, T2* p2, T3* p3, size_t n)
{
while (n-- > 0)
p3[n] = p1[n] + p2[n];
}

template<class T1, class T2, class T3>
void add(T1* p1, T2 v2, T3* p3, size_t n)
{
while (n-- > 0)
p3[n] = p1[n] + v2;
}

void foo(const int *pci, const short *pcs, size_t n)
{
long *pl = new long[n];
add(pci, pcs, pl, n);
delete[] pl;
}

int main ()
{
int a[10];
short b[10];
long c[10];
add(a, b, c, 10);
add(a, 'b', c, 10);

foo(a, b, 10);

return 0;
}
~~~~~~~~~~~~~~~~~

Now, in this case there are three versions of 'add' functions
generated from those two templates, two from 'main' and one more
from 'foo'. Those in 'main' don't have 'const' in any of 'Tx',
and the one generated from the call in 'foo' has 'const' as part
of 'T1' and 'T2'. That should be acceptable, though.

V
 
S

shuisheng

That's because the second function is more specialized than the first
function and the partial ordering rules come into the picture.
Basically in the first one T2 is deduced to be an int
and in the second case T2 is deduced to be an int*...

and if you use substitution rules governed by the partial ordering
mechanism, the second one is more specialized.

What I understant is different from you. The first one is more
specialized if both are allowable for a funcation call. For my calling
case, the second one doesn't match before implicit conversion. So the
compilor choose the second one.

Am I right?

Thanks,

Shuisheng
 

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
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top