 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
mwreese@austin.rr.com Guest
|
Posted: Fri Jun 10, 2005 10:25 am Post subject: implicit type conversion broken for templates? |
|
|
okay, I'll post it. How does this sound for a description:
We've run into a problem with templates using gcc. We can't get type
conversion to work as you would expect with any other type. There
seems to be this hole with respect to templates. Below is a simple
example that illustrates the problem.
If you define "TEMPL", then it compiles the TEMPL code and produces an
error because the implicit conversion operator is not called. If you
don't define TEMPL, then it works.
Any ideas on how to get around this? Implementing the operator is not
an option because this is for a class that has _tons_ of operators and
that would just be icky.
I only tried gcc so this may just be a gcc bug. Thanks for any help!
$ g++ -g -Wall template_bug.C -o run
$ ./run
Making Foo: 10
Making Foo: 20
Making Foo: 2
Cast to Foo
Making Foo: 12
$ g++ -g -Wall template_bug.C -o run
template_bug.C: In function `int main()':
template_bug.C:72: error: no match for 'operator+' in 'a + 2'
#include <iostream>
#include <string>
using namespace std;
#define TEMPL
#ifdef TEMPL
template <size_t N>
struct Foo {
Foo() : _val(0) {};
Foo(int v) : _val(v) { cout << "Making Foo(" << N << "): " << v <<
"n";};
int _val;
};
template
Foo<N1> operator+(Foo<N1> x, Foo<N2> y)
{
return Foo<N1>(x._val + y._val);
}
template <size_t N1>
Foo<N1> operator+(Foo<N1> x,unsigned long y)
{
return Foo<N1>(x._val + y);
}
struct Bar {
Bar() {};
Bar(int x) : _f(x) {};
Bar (Foo<10> f) : _f(f) {};
operator Foo<10>() const { cout << "Cast to Foon"; return _f; };
Foo<10> _f;
};
#else
struct Foo {
Foo() : _val(0) {};
Foo(int v) : _val(v) { cout << "Making Foo: " << v << "n";};
int _val;
};
Foo operator+(Foo x, Foo y)
{
return Foo(x._val + y._val);
}
struct Bar {
Bar() {};
Bar(int x) : _f(x) {};
Bar (Foo f) : _f(f) {};
operator Foo() const { cout << "Cast to Foon"; return _f; };
Foo _f;
};
#endif
int main()
{
Bar a(10);
Bar b(20);
Bar c;
c = /*(Foo<10>)*/a + 2;
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Swampmonster Guest
|
Posted: Sat Jun 11, 2005 10:46 am Post subject: Re: implicit type conversion broken for templates? |
|
|
....
| Quote: | I only tried gcc so this may just be a gcc bug. Thanks for any help!
#include <iostream
#include
using namespace std;
....
template
struct Foo {
Foo() : _val(0) {};
Foo(int v) : _val(v) { cout << "Making Foo(" << N << "): " << v
"n";};
int _val;
};
....
template
Foo
{
return Foo<N1>(x._val + y);
}
struct Bar {
Bar() {};
Bar(int x) : _f(x) {};
Bar (Foo<10> f) : _f(f) {};
operator Foo<10>() const { cout << "Cast to Foon"; return _f; };
Foo<10> _f;
};
....
int main()
{
Bar a(10);
Bar b(20);
Bar c;
c = /*(Foo<10>)*/a + 2;
}
|
MSVC 7.1 does not like the code either. The problem is that it can't
deduce the non-type template argument "size_t N" of:
template <size_t N1>
Foo<N1> operator+(Foo<N1> x,unsigned long y)
{
...
I don't know what the standard says about deducing template arguments
but it seems that neither GCC nor VC 7.1 look for the user defined
conversion when deducing template arguments.
And in some way I think that's good, since there might be a lot of
surprises waiting if they did.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Greg Guest
|
Posted: Sat Jun 11, 2005 10:48 am Post subject: Re: implicit type conversion broken for templates? |
|
|
[email]mwreese (AT) austin (DOT) rr.com[/email] wrote:
| Quote: | okay, I'll post it. How does this sound for a description:
We've run into a problem with templates using gcc. We can't get type
conversion to work as you would expect with any other type. There
seems to be this hole with respect to templates. Below is a simple
example that illustrates the problem.
If you define "TEMPL", then it compiles the TEMPL code and produces an
error because the implicit conversion operator is not called. If you
don't define TEMPL, then it works.
Any ideas on how to get around this? Implementing the operator is not
an option because this is for a class that has _tons_ of operators and
that would just be icky.
I only tried gcc so this may just be a gcc bug. Thanks for any help!
$ g++ -g -Wall template_bug.C -o run
$ ./run
Making Foo: 10
Making Foo: 20
Making Foo: 2
Cast to Foo
Making Foo: 12
$ g++ -g -Wall template_bug.C -o run
template_bug.C: In function `int main()':
template_bug.C:72: error: no match for 'operator+' in 'a + 2'
#include <iostream
#include
using namespace std;
#define TEMPL
#ifdef TEMPL
template
struct Foo {
Foo() : _val(0) {};
Foo(int v) : _val(v) { cout << "Making Foo(" << N << "): " << v
"n";};
int _val;
};
template
Foo
{
return Foo<N1>(x._val + y._val);
}
template <size_t N1
Foo
{
return Foo<N1>(x._val + y);
}
struct Bar {
Bar() {};
Bar(int x) : _f(x) {};
Bar (Foo<10> f) : _f(f) {};
operator Foo<10>() const { cout << "Cast to Foon"; return _f; };
Foo<10> _f;
};
#else
struct Foo {
Foo() : _val(0) {};
Foo(int v) : _val(v) { cout << "Making Foo: " << v << "n";};
int _val;
};
Foo operator+(Foo x, Foo y)
{
return Foo(x._val + y._val);
}
struct Bar {
Bar() {};
Bar(int x) : _f(x) {};
Bar (Foo f) : _f(f) {};
operator Foo() const { cout << "Cast to Foon"; return _f; };
Foo _f;
};
#endif
int main()
{
Bar a(10);
Bar b(20);
Bar c;
c = /*(Foo<10>)*/a + 2;
}
|
The compilation error in the sample source code not a bug in gcc. The
code fails to compile because there is no binary plus operator defined
in the source code that accepts a type bar (on the left side) and type
int (on the right).
Granted, there is a function template that appears to match those types
of arguments via a type conversion, but a function template is not a
function. A function template must first be instantiated for it to
create a function that could be considered as a match for a function
call appearing in the source code.
To instantiate the binary plus operator function template in this case
requires an instantion of Foo as an argument type. Bar is not an
instantiation of class template Foo, even though it is convertible to
Foo<10>. Bar's argument type is Bar. Without Bar being an instantiation
of class template Foo, there is no match of the argument type for the
purposes of function instantiation. With no instantiation, there is no
function that could provide a declaration matching a + 2.
One fix would be to eliminate the instantiaion requirement and simply
declare an overloaded binary plus operator with the appropriate types:
Foo<10> operator+(Foo<10>, int)
{
....
}
With this version of the binary plus operator, the compiler is free to
perform conversions of arguments in order to find a matching function
declaration. Specifically, because Bar can be converted to a Foo<10>
type, the compiler would accept this function as the function that it
cannot find in the current code.
Another approach would be to satisfy the operator+ function template by
making Bar an instantiated Foo type via inheritance:
struct Bar : public Foo<10>
{
...
};
With this change Bar has become an instantiation of class template Foo.
As such the operator+() function template would be instantiated for
arguments of type Bar, and the binary operator plus function would no
longer be missing.
Greg
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Jun 13, 2005 7:59 am Post subject: Re: implicit type conversion broken for templates? |
|
|
Greg wrote:
| Quote: | mwreese (AT) austin (DOT) rr.com wrote:
okay, I'll post it. How does this sound for a description:
We've run into a problem with templates using gcc. We can't
get type conversion to work as you would expect with any
other type. There seems to be this hole with respect to
templates. Below is a simple example that illustrates the
problem.
If you define "TEMPL", then it compiles the TEMPL code and
produces an error because the implicit conversion operator
is not called. If you don't define TEMPL, then it works.
Any ideas on how to get around this? Implementing the
operator is not an option because this is for a class that
has _tons_ of operators and that would just be icky.
I only tried gcc so this may just be a gcc bug. Thanks for
any help!
|
[...]
| Quote: | template <size_t N
struct Foo {
Foo() : _val(0) {};
Foo(int v) : _val(v) { cout << "Making Foo(" << N << "): " << v
"n";};
int _val;
};
template
Foo
{
return Foo<N1>(x._val + y._val);
}
template <size_t N1
Foo
{
return Foo<N1>(x._val + y);
}
struct Bar {
Bar() {};
Bar(int x) : _f(x) {};
Bar (Foo<10> f) : _f(f) {};
operator Foo<10>() const { cout << "Cast to Foon"; return _f; };
Foo<10> _f;
};
int main()
{
Bar a(10);
Bar b(20);
Bar c;
c = /*(Foo<10>)*/a + 2;
}
The compilation error in the sample source code not a bug in
gcc. The code fails to compile because there is no binary plus
operator defined in the source code that accepts a type bar
(on the left side) and type int (on the right).
Granted, there is a function template that appears to match
those types of arguments via a type conversion, but a function
template is not a function. A function template must first be
instantiated for it to create a function that could be
considered as a match for a function call appearing in the
source code.
To instantiate the binary plus operator function template in
this case requires an instantion of Foo as an argument type.
Bar is not an instantiation of class template Foo, even though
it is convertible to Foo<10>. Bar's argument type is Bar.
Without Bar being an instantiation of class template Foo,
there is no match of the argument type for the purposes of
function instantiation. With no instantiation, there is no
function that could provide a declaration matching a + 2.
One fix would be to eliminate the instantiaion requirement and
simply declare an overloaded binary plus operator with the
appropriate types:
Foo<10> operator+(Foo<10>, int)
{
....
}
|
I thought that the classical solution (from Barton and Nackman?
or somewhere else?) was to declare and define the operator
within the template class. That way, the operator itself isn't
(formally, at least) a template function, and a declaration for
it exists for all types the class is instantiated over.
A lot of the time, conversions aren't an issue, and we can just
make the operator a member function. In this case, since we
want implicit conversions to work on the left side, a member
operator doesn't work. The solution is to declare *and* define
the function as a friend, e.g.:
template< size_t N1 >
struct Foo
{
// ...
friend Foo< N1 > operator+( Foo< N1 > x, unsigned long y )
{
// ...
}
} ;
For this to work, you have to define the function in the
template class as well. (Or define each instance externally, but
that sort of defeats the purpuse.)
| Quote: | With this version of the binary plus operator, the compiler is
free to perform conversions of arguments in order to find a
matching function declaration.
|
As you pointed out above, the compiler is always free to perform
conversions among the functions it has. The problem is that it
doesn't have any instances of a template function until that
function has been instantiated, and the compiler doesn't
consider implicit conversions when looking for a function to
instantiate. The trick above is based on a subtle distinction:
the friend function is not a template itself; it is a simple,
everyday function which takes a Foo<N1> and an unsigned long as
parameters. It is declared whenever Foo is instantiated.
FWIW: I'm not sure when the function definition is instantiated.
In particular, if the function contains some construct which is
illegal for certain parameters, is it legal to instantiate the
code over one of those parameters, even though the function
itself is never used? This is the case of non-virtual member
functions, but the paragraphs that I find in the standard which
discuss this, particularly §14.7.1/9, speak only of "a function
template, a member template, a non-virtual member function, a
member class or a static data member of a class template". An
embedded friend function is none of these. (Which makes me
worry. I've got some code which counts on the function not
being instantiated if it isn't used. Which is what g++ and Sun
CC do, and what seems logically consistant, exterpolating from
the behavior of non-virtual member functions.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
ta0kira@yahoo.com Guest
|
Posted: Mon Jun 13, 2005 4:17 pm Post subject: Re: implicit type conversion broken for templates? |
|
|
gcc doesn't like to convert more than once implicitly to deduce a
template parameter...
ta0kira
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|