Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Implicit conversion for operator

1 view
Skip to first unread message

pikalaw

unread,
Jul 24, 2009, 8:26:01 PM7/24/09
to
Need help figuring out why the following code fails to compile:

#include <complex>
#include <boost/rational.hpp>

int main()
{
using namespace std;
using namespace boost;

complex<rational<int> > z;
const complex<rational<int> > j(0,1);

z = 3 + 2 * j; // (1). won't
compile. complains about no matching operator*.
z = 3 + rational<int>(2) * j; // (2). but this works.
note the explicit conversion.

return 0;
}

The compiler error, generated by gcc version 4.0.1 (Apple Inc. build
5490), was

boost/operators.hpp:251: note:
candidates are: boost::rational<int> boost::operator*(const
boost::rational<int>&, const int&)
boost/operators.hpp:251: note:
boost::rational<int> boost::operator*(const int&, const
boost::rational<int>&)
boost/operators.hpp:251: note:
boost::rational<int> boost::operator*(const
boost::rational<int>&, const boost::rational<int>&)

What bugs [pun] me is why didn't the compiler try the operator

std::complex<boost::rational<int> >
std::operator*(
const boost::rational<int>&,
const std::complex<boost::rational<int> >&)

for (1) by promoting the integer constant 2 to rational<int>? But if
I explicitly convert it as in (2), then it finds this operator.

Please illuminate me!

pikalaw

unread,
Jul 24, 2009, 10:24:06 PM7/24/09
to
An update:

Even this simplified code does not compile.

#include <complex>

using namespace std;

int main()
{
complex<long > y;
const complex<long > k(0,1);

y = 2 * k;

return 0;
}

But if I replace the constant 2 with 2L, the long version, then it
compiles.

Ian Collins

unread,
Jul 24, 2009, 11:09:21 PM7/24/09
to
pikalaw wrote:
> An update:
>
> Even this simplified code does not compile.
>
> #include <complex>
>
> using namespace std;
>
> int main()
> {
> complex<long > y;
> const complex<long > k(0,1);
>
> y = 2 * k;
>
> return 0;
> }
>
> But if I replace the constant 2 with 2L, the long version, then it
> compiles.

What else did you expect? int != long.

--
Ian Collins

Victor Bazarov

unread,
Jul 24, 2009, 11:14:51 PM7/24/09
to

The library only defines, literally:

template<class T> complex<T> operator*(const T&, const complex<T>&);

operator for multiplication. For the expression '2 * j' it tries to
come to a conclusion what 'T' should be. It can be 'int', or it can be
'rational<int>'. The implicit conversions are not applied when deducing
template arguments. If 'T' is 'int' (from the first argument), the
second argument does not match. If it's 'rational<int>' (from the
second argument), the first argument's type does not match. So, the
compiler discards the template operator*. No other operator* suits the
expression since 'complex<rational<int> >' is not convertible to any of
the multipliers of 'int'.

If it's unclear, ask more questions.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask

Stephen Horne

unread,
Jul 24, 2009, 11:19:38 PM7/24/09
to
On Fri, 24 Jul 2009 19:24:06 -0700 (PDT), pikalaw <pik...@gmail.com>
wrote:

>An update:
>
>Even this simplified code does not compile.

> complex<long > y;


> const complex<long > k(0,1);
>
> y = 2 * k;

>But if I replace the constant 2 with 2L, the long version, then it
>compiles.

When you use the literal 2, I suspect two conversions are needed...

2 -> 2L (from integer to long)

2L -> <complex 2> (from long to complex)

IIRC, conversions are only done implicitly if they can be achieved in
a single step.

pikalaw

unread,
Jul 25, 2009, 1:22:04 AM7/25/09
to

It seems that implicit type-conversion is only allowed with non-
template functions. Had I declared

complex<long> operator*(long a, const complex<long> &z)

then the expression '2 * k' compiles fine.

However, with template version of functions, implicit type-conversion
is not done.

BTW, is this behavior in the C++ standard? Or one of those
unspecified, implementation-dependent behavior?

pikalaw

unread,
Jul 25, 2009, 1:22:48 AM7/25/09
to

Well, but int can implicitly convert to long, no?

> --
> Ian Collins

pikalaw

unread,
Jul 25, 2009, 1:27:32 AM7/25/09
to
On Jul 24, 11:19 pm, Stephen Horne <sh006d3...@blueyonder.co.uk>
wrote:
> On Fri, 24 Jul 2009 19:24:06 -0700 (PDT), pikalaw <pika...@gmail.com>

> wrote:
>
> >An update:
>
> >Even this simplified code does not compile.
> >                complex<long >  y;
> >        const   complex<long >  k(0,1);
>
> >        y = 2 * k;
> >But if I replace the constant 2 with 2L, the long version, then it
> >compiles.
>
> When you use the literal 2, I suspect two conversions are needed...
>
>   2 -> 2L  (from integer to long)
>
>   2L -> <complex 2> (from long to complex)
>
> IIRC, conversions are only done implicitly if they can be achieved in
> a single step.

I checked the stl source, the template defines the operator template

complex<T> operator*(const T &, const complex<T>);

So, I had expected this template to instantiate into

complex<long> operator*(const long &, const complex<long> &)

which with one implicit conversion from int to long would allow '2 *
k' be called.

But it didn't.

James Kanze

unread,
Jul 25, 2009, 4:16:07 AM7/25/09
to
On Jul 25, 7:27 am, pikalaw <pika...@gmail.com> wrote:
> On Jul 24, 11:19 pm, Stephen Horne <sh006d3...@blueyonder.co.uk>
> wrote:
> > On Fri, 24 Jul 2009 19:24:06 -0700 (PDT), pikalaw <pika...@gmail.com>
> > wrote:

> > >An update:

> > >Even this simplified code does not compile.
> > > complex<long > y;
> > > const complex<long > k(0,1);

> > > y = 2 * k;
> > >But if I replace the constant 2 with 2L, the long version,
> > >then it compiles.

> > When you use the literal 2, I suspect two conversions are
> > needed...

> > 2 -> 2L (from integer to long)

> > 2L -> <complex 2> (from long to complex)

> > IIRC, conversions are only done implicitly if they can be
> > achieved in a single step.

No. The restriction only applies to user defined conversions.
If complex wasn't a template (or if the called function wasn't a
template), then there would be no problem.

> I checked the stl source, the template defines the operator template

> complex<T> operator*(const T &, const complex<T>);

> So, I had expected this template to instantiate into

> complex<long> operator*(const long &, const complex<long> &)

> which with one implicit conversion from int to long would
> allow '2 * k' be called.

The problem is precisely that type deduction doesn't succeed for
the template. Type deduction using the first argument results
in T == int, and type deduction using the second argument
results in T == long. The compiler cannot choose between them.
(In type deduction, very few conversions are considered;
considering all conversions would far too often result in
ambiguity.) And since type deduction fails, there is no
instantiation (i.e. no function) for the compiler to do overload
resolution on.

--
James Kanze (GABI Software) email:james...@gmail.com
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

James Kanze

unread,
Jul 25, 2009, 4:20:37 AM7/25/09
to
On Jul 25, 7:22 am, pikalaw <pika...@gmail.com> wrote:
> On Jul 24, 11:14 pm, Victor Bazarov <v.Abaza...@comAcast.net> wrote:

[...]


> It seems that implicit type-conversion is only allowed with
> non- template functions. Had I declared

> complex<long> operator*(long a, const complex<long> &z)
>
> then the expression '2 * k' compiles fine.

> However, with template version of functions, implicit
> type-conversion is not done.

> BTW, is this behavior in the C++ standard? Or one of those
> unspecified, implementation-dependent behavior?

It's the required behavior, according to the standard. You're
confusing two separate issues: operator overload resolution and
template type deduction. A function template is NOT a function,
and is not considered directly by operator overloading. If
there is a function template in scope, the compiler will try to
deduce its arguments, in order to instantiate it; if this
succeeds, the instantiated instance (which is a function) will
be added to the overload set, to be considered by overload
resolution. Very few conversions are considered (cv-qualifier
conversions, I think, and not much else) for template argument
deduction, because otherwise, one would end up with far too many
possibilities.

Alf P. Steinbach

unread,
Jul 25, 2009, 4:47:18 AM7/25/09
to
* James Kanze:

> On Jul 25, 7:22 am, pikalaw <pika...@gmail.com> wrote:
>> On Jul 24, 11:14 pm, Victor Bazarov <v.Abaza...@comAcast.net> wrote:
>
> [...]
>> It seems that implicit type-conversion is only allowed with
>> non- template functions. Had I declared
>
>> complex<long> operator*(long a, const complex<long> &z)
>>
>> then the expression '2 * k' compiles fine.
>
>> However, with template version of functions, implicit
>> type-conversion is not done.
>
>> BTW, is this behavior in the C++ standard? Or one of those
>> unspecified, implementation-dependent behavior?
>
> It's the required behavior, according to the standard. You're
> confusing two separate issues: operator overload resolution and
> template type deduction. A function template is NOT a function,
> and is not considered directly by operator overloading. If
> there is a function template in scope, the compiler will try to
> deduce its arguments, in order to instantiate it; if this
> succeeds, the instantiated instance (which is a function) will
> be added to the overload set, to be considered by overload
> resolution. Very few conversions are considered (cv-qualifier
> conversions, I think, and not much else) for template argument
> deduction, because otherwise, one would end up with far too many
> possibilities.

One of them (it's actually worthwhile to know about in order to write templates
that work more generally than usual, but then requires some support):

<code>
template< typename T >
struct B {};

template< typename T >
void foo( B<T> const& ) {}

struct D: B<double> {};

int main()
{
foo( D() );
}
</code>


Cheers,

- Alf

Paul Brettschneider

unread,
Jul 25, 2009, 4:55:18 AM7/25/09
to
pikalaw wrote:

> An update:
>
> Even this simplified code does not compile.
>
> #include <complex>
>
> using namespace std;
>
> int main()
> {
> complex<long > y;
> const complex<long > k(0,1);
>
> y = 2 * k;
>
> return 0;
> }
>
> But if I replace the constant 2 with 2L, the long version, then it
> compiles.

Yes, that is not very comfortable. You could solve the problem in a very
hackish way:
template <typename T1,typename T2>
typename std::complex<T1> operator*(typename std::complex<T1> c, T2 v)
{
return c * T1(v);
}
template <typename T1,typename T2>
typename std::complex<T1> operator*(T2 v, typename std::complex<T1> c)
{
return c * T1(v);
}

One major problem with this approach is that when you multiply a
complex<short> with a long, you would expect to obtain a complex<long> and
not a truncated complex<short>.

Ian Collins

unread,
Jul 25, 2009, 5:32:57 AM7/25/09
to

Then add a further bit of hackery:

template <bool b> struct X;

template <> struct X<1> {};

template <typename T1,typename T2>
typename std::complex<T1> operator*(T2 v, typename std::complex<T1> c)
{

X<sizeof(T1)>=sizeof(T2)> x;

return c * T1(v);
}


--
Ian Collins

pikalaw

unread,
Jul 25, 2009, 1:18:36 PM7/25/09
to
On Jul 25, 4:20 am, James Kanze <james.ka...@gmail.com> wrote:

> On Jul 25, 7:22 am,pikalaw<pika...@gmail.com> wrote:
>
> > On Jul 24, 11:14 pm, Victor Bazarov <v.Abaza...@comAcast.net> wrote:
>
>     [...]
>
> If
> there is a function template in scope, the compiler will try to
> deduce its arguments, in order to instantiate it; if this
> succeeds, the instantiated instance (which is a function) will
> be added to the overload set, to be considered by overload
> resolution.  

I'm not so sure that an instantiated instance will be considered for
overlaoding. The following code does not compile:

template<class T>
void f(T, T) { }

template void f(long, long); // let's force an instantiation.

int main()
{
f(1L, 2L); // ok.
f(1L, 2); // no matching function

return 0;
}

> [...]
>
> --
> James Kanze (GABI Software)             email:james.ka...@gmail.com

pikalaw

unread,
Jul 25, 2009, 1:23:29 PM7/25/09
to
On Jul 25, 4:16 am, James Kanze <james.ka...@gmail.com> wrote:

> On Jul 25, 7:27 am,pikalaw<pika...@gmail.com> wrote:
>
>
>
>
>
> > On Jul 24, 11:19 pm, Stephen Horne <sh006d3...@blueyonder.co.uk>
> > wrote:
> > > On Fri, 24 Jul 2009 19:24:06 -0700 (PDT),pikalaw<pika...@gmail.com>

Thanks. That clears it up.

>
> --
> James Kanze (GABI Software)             email:james.ka...@gmail.com

James Kanze

unread,
Jul 26, 2009, 3:32:54 AM7/26/09
to
On Jul 25, 7:18 pm, pikalaw <pika...@gmail.com> wrote:
> On Jul 25, 4:20 am, James Kanze <james.ka...@gmail.com> wrote:

> > On Jul 25, 7:22 am,pikalaw<pika...@gmail.com> wrote:

> > > On Jul 24, 11:14 pm, Victor Bazarov <v.Abaza...@comAcast.net> wrote:

> > [...]

> > If there is a function template in scope, the compiler will
> > try to deduce its arguments, in order to instantiate it; if
> > this succeeds, the instantiated instance (which is a
> > function) will be added to the overload set, to be
> > considered by overload resolution.

> I'm not so sure that an instantiated instance will be
> considered for overlaoding.

Of course it is. Otherwise, it couldn't be called.

> The following code does not compile:

> template<class T>
> void f(T, T) { }

> template void f(long, long); // let's force an instantiation.
>
> int main()
> {
> f(1L, 2L); // ok.
> f(1L, 2); // no matching function

> return 0;
> }
> > [...]

OK. I see what you're getting at. And you're right: we need to
find a clearer way of explaining it. The expression f(1L, 2L)
triggers the instantiation of f(long, long), and that function
is considered instantiated within that expression. This fact is
not taken into consideration in any other expression, however;
for f(long, long) to be taken into consideration in the
expression f(1L, 2), that expression must trigger the
instantiation---possible instantiations elsewhere don't count.

Technically, it would be very hard to specify if they did---what
about instantiations triggered from a different translation
unit, or even in a different function? And very hard to
implement. And would also cause no end of problems for
understanding the code---modifications in another function could
change the semantics of this function.

--
James Kanze (GABI Software) email:james...@gmail.com

0 new messages