template error

A

asit

Why the following code shows terrific error ???

//minmax.h

template <class T>
T max(T a, T b)
{
if(a>b)
return a;
else
return b;
}

template <class T>
T min(T a, T b)
{
if(a>b)
return b;
else
return a;
}

//tminmax.cpp
#include <iostream>
#include "minmax.h"

using namespace std;

int main()
{
int i1=100, i2=200;
double d1=3.14159, d2=9.87654;
char c1='A', c2='z';

cout<<"max(i1, i2) == "<<max(i1, i2)<<endl;
cout<<"max(d1, d2) == "<<max(d1, d2)<<endl;
cout<<"max(c1, c2) == "<<max(c1, c2)<<endl;

cout<<"min(i1, i2) == "<<min(i1, i2)<<endl;
cout<<"min(d1, d2) == "<<min(d1, d2)<<endl;
cout<<"min(c1, c2) == "<<min(c1, c2)<<endl;

return 0;
}

When I compiled it, it gave me the following o/p

C:\cpp>g++ -o tminmax.exe tminmax.cpp
tminmax.cpp: In function `int main()':
tminmax.cpp:12: error: call of overloaded `max(int&, int&)' is
ambiguous
minmax.h:3: note: candidates are: T max(T, T) [with T = int]
F:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
bits/stl_a
lgobase.h:173: note: const _Tp& std::max(const _Tp&,
const _Tp&)
[with _Tp = int]
tminmax.cpp:13: error: call of overloaded `max(double&, double&)' is
ambiguous
minmax.h:3: note: candidates are: T max(T, T) [with T = double]
F:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
bits/stl_a
lgobase.h:173: note: const _Tp& std::max(const _Tp&,
const _Tp&)
[with _Tp = double]
tminmax.cpp:14: error: call of overloaded `max(char&, char&)' is
ambiguous
minmax.h:3: note: candidates are: T max(T, T) [with T = char]
F:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
bits/stl_a
lgobase.h:173: note: const _Tp& std::max(const _Tp&,
const _Tp&)
[with _Tp = char]
tminmax.cpp:16: error: call of overloaded `min(int&, int&)' is
ambiguous
minmax.h:12: note: candidates are: T min(T, T) [with T = int]
F:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
bits/stl_a
lgobase.h:151: note: const _Tp& std::min(const _Tp&,
const _Tp&)
[with _Tp = std::streamsize]
tminmax.cpp:17: error: call of overloaded `min(double&, double&)' is
ambiguous
minmax.h:12: note: candidates are: T min(T, T) [with T = double]
F:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
bits/stl_a
lgobase.h:151: note: const _Tp& std::min(const _Tp&,
const _Tp&)
[with _Tp = double]
tminmax.cpp:18: error: call of overloaded `min(char&, char&)' is
ambiguous
minmax.h:12: note: candidates are: T min(T, T) [with T = char]
F:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
bits/stl_a
lgobase.h:151: note: const _Tp& std::min(const _Tp&,
const _Tp&)
[with _Tp = char]
 
S

SG

Hello asit,

I shortened your code a bit but it's practically the same:

#include <iostream>

template <class T>
T max(T a, T b)
{
        if(a>b) return a;
        else    return b;
}

template <class T>
T min(T a, T b)
{
        if(a>b) return b;
        else    return a;
}

You should have used the less than operator < here. It's common
practise to use < for ordering. Some types might only support less
than.
using namespace std;

You're making every name in std:: accessible without qualification.
This may include std::min and std::max you otherwise find in the
int main()
{
        int i1=100, i2=200;
        double d1=3.14159, d2=9.87654;
        char c1='A', c2='z';

        cout<<"max(i1, i2) == "<<max(i1, i2)<<endl;
        cout<<"max(d1, d2) == "<<max(d1, d2)<<endl;
        cout<<"max(c1, c2) == "<<max(c1, c2)<<endl;

        cout<<"min(i1, i2) == "<<min(i1, i2)<<endl;
        cout<<"min(d1, d2) == "<<min(d1, d2)<<endl;
        cout<<"min(c1, c2) == "<<min(c1, c2)<<endl;

        return 0;
}

When I compiled it, it gave me the following o/p

C:\cpp>g++ -o tminmax.exe tminmax.cpp
tminmax.cpp: In function `int main()':
tminmax.cpp:12: error: call of overloaded `max(int&, int&)' is
ambiguous
minmax.h:3: note: candidates are: T max(T, T) [with T = int]
lgobase.h:173: note:             const _Tp& std::max(const _Tp&,
const _Tp&) [with _Tp = int]

There's your explanation. The compiler doesn't know whether you meant
the template you wrote in the global scope or std::max. Both are
accessible due to the using directive.

The name lookup rules are a bit complicated. Let me try to give a
simple explanation for this situation: A using directive for a
namespace X makes the compiler look into the namespace X as well when
it has to search the global scope.

You can solve this problem in a number of ways. I'm going to mention
three:

(A) Replace the using directive with a couple of using
declarations:

using std::cout;
using std::endl;

(B) Keep the using directive and put your functions and classes
in your own namespace N. When you use an unqualified name X
inside your namespace N, the names in std:: will only be
considered in case your own namespace doesn't contain an X.
If your namespace contains an X it will "hide" other Xs from
the global scope.

(C) Keep the using directive and make sure that you don't
use names that also live in the std:: namespace.


Cheers!
SG
 
J

Juha Nieminen

SG said:
You can solve this problem in a number of ways. I'm going to mention
three:

(A) Replace the using directive with a couple of using
declarations:

using std::cout;
using std::endl;

(B) Keep the using directive and put your functions and classes
in your own namespace N. When you use an unqualified name X
inside your namespace N, the names in std:: will only be
considered in case your own namespace doesn't contain an X.
If your namespace contains an X it will "hide" other Xs from
the global scope.

(C) Keep the using directive and make sure that you don't
use names that also live in the std:: namespace.

How about not using the 'using' directive at all? My code has become
much more readable after I stopped using it. Don't believe dissenting
opinions, they are wrong. The code *does* become more readable.
 
J

Jerry Coffin

[ ... ]
How about not using the 'using' directive at all? My code has
become much more readable after I stopped using it. Don't believe
dissenting opinions, they are wrong. The code *does* become more
readable.

There are times that a using directive (or at least a using
declaration) can be useful, and there's no real alternative to it.

The canonical example is something like a sort routine. If swap has
been specialized for the type being sorted, we want to use that swap.
If swap has not been specialized for the type, we want to fall back
on using std::swap instead.

We can get that with code something like this:

using std::swap;

template <class T>
void sort(/* ... */) {

// ...
// x1 and x2 are T's.
if (x2 < x1)
swap(x1, s2);
// ...
}

The trick here is simple: if we specify std::swap, then that's what
will be used, even if T provides its own swap. On the other hand, if
we try to specify T's namespace, the code won't compile unless T
actually _does_ provide its own swap (and we don't want that
restriction -- just for example, it would then fail for all built-in
types).

The cure is pretty simple, as shown above. If there's a 'swap' in T's
namespace, argument dependent lookup will find it, and that's what
will get used. Otherwise, because of the using declaration, std::swap
will be found instead.

In the case above, I've used a using declaration, but if (for
example) we were using quite a few different things like this, a
using directive would become more convenient. Just for example, the
code above uses the less-than operator, but for genericity it should
really use a comparison function. Again, std::less<T> might be the
appropriate version (it would be equivalent to what's above) but
again, if T provides its own less(), we'd prefer to use that. In this
case there's not nearly as big a problem though, simply because
std::less<T> will use T's operator< (so all that's usually needed is
to supply operator< if ordering is at all appropriate).
 
R

red floyd

Jerry said:
There are times that a using directive (or at least a using
declaration) can be useful, and there's no real alternative to it.

The canonical example is something like a sort routine. If swap has
been specialized for the type being sorted, we want to use that swap.
If swap has not been specialized for the type, we want to fall back
on using std::swap instead.

We can get that with code something like this:

using std::swap;

template <class T>
void sort(/* ... */) {

// ...
// x1 and x2 are T's.
if (x2 < x1)
swap(x1, s2);
// ...
}


Isn't this the one case where the user is allowed to add to std::?
That is, specializing a template in std::? Why not just specialize
std::swap<> for your type T, instead? e.g.:

class my_expensive_to_swap_class {
// ...
public:
void swap(T&);
};

template<>
std::swap<my_expensive_to_swap_class>(
my_expensive_to_swap_class& x,
my_expensive_to_swap_class& y)
{
x.swap(y);
}

// etc....
 
A

Alf P. Steinbach

* red floyd:
Isn't this the one case where the user is allowed to add to std::?
That is, specializing a template in std::? Why not just specialize
std::swap<> for your type T, instead? e.g.:

class my_expensive_to_swap_class {
// ...
public:
void swap(T&);
};

template<>
std::swap<my_expensive_to_swap_class>(
my_expensive_to_swap_class& x,
my_expensive_to_swap_class& y)
{
x.swap(y);
}

// etc....

I agree that this would be nice, and that it's even what one would choose to do
in practice.

But if there's a language lawyer nearby it may be highly provocative.

For the standard (probably through some mishap) only allows specialization of
'std' namespace classes, not routines, and so, perhaps because that arbitrary
restriction appears to be very mysterious and subtle to those who think the
standard must be perfect where it's not obviously self-contradictory, the
established Perfect Standard Code practice is to Not Do That(TM).


Cheers & hth.,

- Alf
 
S

SG

But if there's a language lawyer nearby it may be highly provocative.

For the standard (probably through some mishap) only allows specialization of
'std' namespace classes, not routines,

Are you sure? Can you quote the standard on that?

If my memory serves me well, Scott Meyers wrote in "Effective C++"
that it is legal to specialize a function template in std::.

Cheers!
SG
 
J

James Kanze

Jerry Coffin wrote:
Isn't this the one case where the user is allowed to add to
std::? That is, specializing a template in std::? Why not
just specialize std::swap<> for your type T, instead? e.g.:
class my_expensive_to_swap_class {
// ...
public:
void swap(T&);
};
template<>
std::swap<my_expensive_to_swap_class>(
my_expensive_to_swap_class& x,
my_expensive_to_swap_class& y)
{
x.swap(y);
}
// etc....

That's what I'd generally recommend, but it doesn't work if the
class in question is actually a template; you can specialize a
function in std:: over a single type, but you can't provide an
overload which would work with a template type (officially, at
least---in practice, you almost certainly can).
 
A

Alf P. Steinbach

* SG:
Are you sure?

No, I made a too sweeping statement. :)

Can you quote the standard on that?

Yeah. §17.4.3.1/1 states that "A program may add template specializations for
any standard library template to namespace std."

And this works fine for Jerry's example (at least when fixed, it was evidently
written in haste just as my article), sorry for that over-generalization, mea culpa.

/However/, what I should have written if my memory hadn't over-generalized, was
that you cannot, within strict standard-conformance, do e.g.

namespace std {
template< typename T > void swap( MyArray<T>&, MyArray<T>& );
}

And the reason is that this is /not/ a template specialization (as Jerry's
example was) -- in particular, it's not a partial specialization.

This is an overload.

If my memory serves me well, Scott Meyers wrote in "Effective C++"
that it is legal to specialize a function template in std::.

He he. I think we're perhaps in the "slow zone" of the galaxy. But, our Usenet
ruminations will perhaps find their way all the way up to Top Relay. :)


Cheers & thanks,

- Alf
 
J

James Kanze

* red floyd:
I agree that this would be nice, and that it's even what one
would choose to do in practice.
But if there's a language lawyer nearby it may be highly provocative.
For the standard (probably through some mishap) only allows
specialization of 'std' namespace classes, not routines,

That's wrong. You're mixing it up with another issue.
and so, perhaps because that arbitrary restriction appears to
be very mysterious and subtle to those who think the standard
must be perfect where it's not obviously self-contradictory,
the established Perfect Standard Code practice is to Not Do
That(TM).

The problem is when the user defined type isn't a type, but a
template. Something like:

class MyClass { /* ... */ } ;
namespace std {
void swap( MyClass& a, MyClass& b ) { /* ... */ }
}

is perfectly legal, and approved by the standard. If, on the
other hand, you have something like:

template< typename T >
class MyClass { /* ... */ } ;
namespace std {
template< typename T >
void swap( MyClass< T >& a, MyClass< T >& b )
{ /* ... */ }
}

you don't have an explicit specialization, you've defined a new
function template (which overloads with the existing one). And
that is formally forbidden (but is IMHO still the preferred
solution---since it will, in practice, work).

It's interesting, because I think with a little work, the
wording could be extended to officially allow the above. No one
has done that work, however.
 
J

Jerry Coffin

[ ... ]
Isn't this the one case where the user is allowed to add to std::?
That is, specializing a template in std::? Why not just specialize
std::swap<> for your type T, instead? e.g.:

It is a case where it's allowed, and things work just fine if the
user has done that. Keep in mind, however, that when you're writing a
template, you have essentially no control over what the user will
decide to do, and you want your template to "do the right thing"
anyway.
 

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
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top