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

confused by template type parameter deduction for std::complex

73 views
Skip to first unread message

萌 朱

unread,
May 24, 2012, 2:41:40 PM5/24/12
to
The following simple program failed to compile with
g++-4.7.0 -std=c++11

// ex.1
#include <complex>
int main ()
{
std::complex<float> const c(-1./2,sqrt(3.)/2);
std::complex<float> const d = std::pow(c,1./2);
return 0;
}

with "error: conversion from 'std::complex<double>' to non-scalar
type 'const std::complex<float>' requested"

which I understand because the constructor of std::complex<float>
taking a std::complex<double> is explicit and I also know the
workaround.

What confused me is, it seems to me that the T parameter for
std::pow is deduced as double, while I expect it to be deduced
as float. Here is my logic.

For ease of discussion, here are the overloads of std::pow
involving a std::complex<T>.

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

For the first one, I expect T is deduced as float from c and
double from 1./2, which then causes a deduction failure,
so this one is gone. For the second one, T is deduced
as float and it gets into the candidate set.
The third one failed clearly.

Therefore, the only viable function is the second version's
float specialization.
A temporary of type complex<float> then needs to be constructed from
the literal 1./2, which is fine because complex<float> has constructor
that can do so. But the return type should be complex<float> too,
so this call should be ok.

I then have the following slightly different program where I
explicitly called std::pow specialized to float.

// ex.2
#include <complex>
int main ()
{
std::complex<float> const c(-1./2,sqrt(3.)/2);
std::complex<float> const d = std::pow<float>(c,1./2);
return 0;
}

with g++-4.7.0 -std=c++11. Again, I got the same error message.
This is even more confusing because I do not know how does the
std::complex<double> type come into play in this call.
I expect pow(complex<float> const&, float const&) is chosen
and the second parameter binds to a temporary created from 1./2.
The second version of pow is not favored because constructing
a complex<float> from 1./2 involves a user-defined conversion.

On the other hand, this program compiles just fine and runs correctly.
// ex.3
#include <complex>
int main ()
{
std::complex<float> const c(-1./2,sqrt(3.)/2);
std::complex<float> const d = std::pow(c,1.f/2); // note the f suffix
return 0;
}
which is expected, of course.

Moreover, if I compile ex.1 without the -std=c++11 flag using
g++-4.7.0 (c++03 mode I assume).
It is compiled ok, but running it shows that the d variable is
computed to be (1,0) and the only reason I can think of is because
the pow(const complex<T>&,int) overload is called, which makes
the 1./2 to be a 0 effectively. (btw, that overload is not in c++11)
And the way I understand this choice of the compiler is that,
the above mentioned deduction failure happened, therefore, the
candidate functions are the "int" one and the second version listed
above. Overload resolution then chose the "int" one because a 1./2
to int is only an implicit standard conversion. Am I right?

Thank you for any pointers or comments.

Best
Meng


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
May 24, 2012, 3:56:25 PM5/24/12
to
Am 24.05.2012 20:41, schrieb 萌 朱:
> The following simple program failed to compile with
> g++-4.7.0 -std=c++11
>
> // ex.1
> #include<complex>
> int main ()
> {
> std::complex<float> const c(-1./2,sqrt(3.)/2);
> std::complex<float> const d = std::pow(c,1./2);
> return 0;
> }
>
> with "error: conversion from 'std::complex<double>' to non-scalar
> type 'const std::complex<float>' requested"

Let me nit-pick first that this program is already ill-formed because
there is no guarantee for the declaration of a function sqrt being
available. I'm adding an assumed

#include <math.h>

> which I understand because the constructor of std::complex<float>
> taking a std::complex<double> is explicit and I also know the
> workaround.

You are correct. Note also that in C++03 the function call
std::pow(c,1./2) would be ambiguous, because the compiler wouldn't find
a single best match for this overload set:

template<class T> complex<T> pow(const complex<T>&, int);
template<class T> complex<T> pow(const complex<T>&, const T&);
template<class T> complex<T> pow(const complex<T>&, const complex<T>&);
template<class T> complex<T> pow(const T&, const complex<T>&);

Ignoring the last one, either of

template<> complex<float> pow(const complex<float>&, int);
template<> complex<float> pow(const complex<float>&, const float&);
template<> complex<double> pow(const complex<double>&, const
complex<double>&);

would be considered and none (especially of the first two) is better
than the other compared to the arguments const std::complex<float> and
double.

> What confused me is, it seems to me that the T parameter for
> std::pow is deduced as double, while I expect it to be deduced
> as float. Here is my logic.
>
> For ease of discussion, here are the overloads of std::pow
> involving a std::complex<T>.
>
> template<class T> complex<T> pow(const complex<T>&, const T&);
> template<class T> complex<T> pow(const complex<T>&, const
> complex<T>&);
> template<class T> complex<T> pow(const T&, const complex<T>&);
>
> For the first one, I expect T is deduced as float from c and
> double from 1./2, which then causes a deduction failure,
> so this one is gone. For the second one, T is deduced
> as float and it gets into the candidate set.
> The third one failed clearly.

Your deduction chain is wrong. First, if above overloads would be the
sole one existing, you would have a similar problem as in C++03, because
there would be no single best match and there would be an ambiguity
again. The solution for this is described in [cmplx.over] p3 of C++11,
where we have the following said:

"Function template pow shall have additional overloads sufficient to
ensure, for a call with at least one argument of type complex<T>:

1. If either argument has type complex<long double> or type long double,
then both arguments are effectively cast to complex<long double>.
2. Otherwise, if either argument has type complex<double>, double, or an
integer type, then both arguments are effectively cast to complex<double>.
3. Otherwise, if either argument has type complex<float> or float, then
both arguments are effectively cast to complex<float>."

If you go though these bullets, the first match is bullet two, because
we have a second argument of type double. Therefore the code should
behave as if you would have called

std::pow(static_cast<complex<double>>(c),
static_cast<complex<double>>(1./2));

ending in the overload

template<> complex<double> pow(const complex<double>&, const
complex<double>&);

> I then have the following slightly different program where I
> explicitly called std::pow specialized to float.
>
> // ex.2
> #include<complex>
> int main ()
> {
> std::complex<float> const c(-1./2,sqrt(3.)/2);
> std::complex<float> const d = std::pow<float>(c,1./2);
> return 0;
> }
>
> with g++-4.7.0 -std=c++11. Again, I got the same error message.
> This is even more confusing because I do not know how does the
> std::complex<double> type come into play in this call.
> I expect pow(complex<float> const&, float const&) is chosen
> and the second parameter binds to a temporary created from 1./2.
> The second version of pow is not favored because constructing
> a complex<float> from 1./2 involves a user-defined conversion.

The explicit template parameter does not solve the problem. In C++03 it
would still be ambiguous because of the three overloads

template<> complex<float> pow(const complex<float>&, int);
template<> complex<float> pow(const complex<float>&, const float&);
template<> complex<float> pow(const complex<float>&, const complex<float>&);

In C++11 the code behaviour is unspecified, because from above wording
in [cmplx.over] p3 there is an unspecified number of further functions
or function templates implied. Don't write it that way in portable code.

> Moreover, if I compile ex.1 without the -std=c++11 flag using
> g++-4.7.0 (c++03 mode I assume).
> It is compiled ok, but running it shows that the d variable is
> computed to be (1,0) and the only reason I can think of is because
> the pow(const complex<T>&,int) overload is called, which makes
> the 1./2 to be a 0 effectively. (btw, that overload is not in c++11)

IMO this behaviour is incorrect. In C++03 this should be ambigious as
explained above.

HTH & Greetings from Bremen,

Daniel Krügler

萌 朱

unread,
May 25, 2012, 12:30:04 AM5/25/12
to
> Let me nit-pick first that this program is already ill-formed
> because there is no guarantee for the declaration of a function sqrt
> being available. I'm adding an assumed
>
> #include <math.h>

Thanks. Would it be better to #include <cmath> then?

> You are correct. Note also that in C++03 the function call
> std::pow(c,1./2) would be ambiguous, because the compiler wouldn't
> find a single best match for this overload set:
>
> template<class T> complex<T> pow(const complex<T>&, int);
> template<class T> complex<T> pow(const complex<T>&, const T&);
> template<class T> complex<T> pow(const complex<T>&, const complex<T>&);
> template<class T> complex<T> pow(const T&, const complex<T>&);
>
> Ignoring the last one, either of
>
> template<> complex<float> pow(const complex<float>&, int);
> template<> complex<float> pow(const complex<float>&, const float&);
> template<> complex<double> pow(const complex<double>&, const
> complex<double>&);
>
> would be considered and none (especially of the first two) is better
> than the other compared to the arguments const std::complex<float>
> and double.

I have questions. I do not see how did you get the candidate
functions.
Especially how did you get the complex<double> specialization.
My understanding is that for function templates, parameter deduction
comes first, which for this set of function templates, is
(1) for
template<class T> complex<T> pow(const complex<T>&, int);
T is deduced as float from the actual argument c and the deduction
finished, so the deduction succeeded and this adds
template<> complex<float> pow(const complex<float>&, int);
to the candidate function set.
(2) for
template<class T> complex<T> pow(const complex<T>&, const T&);
T is again deduced as float from c and as double from 1./2
therefore, the deduction failed as T cannot be consistently
deduced. so this one produces no functions.
(3) for
template<class T> complex<T> pow(const complex<T>&, const
complex<T>&);
T is deduced as float from c and failed for 1./2 because there is
no type such that complex<type> would match double. therefore,
deduction failed and this adds no function to the candidate set.

Now overload resolution takes place, it sees only
template<> complex<float> pow(const complex<float>&, int);

and noticed that double has implicit conversion to int. This is to say
this call is valid if only those four function templates are
considered.
I am learning template deduction rules and how they work in action.
I appreciate if you can point me with detailed explanation where I was
wrong in my logic. Thanks.

> Your deduction chain is wrong. First, if above overloads would be
> the sole one existing, you would have a similar problem as in C++03,
> because there would be no single best match and there would be an
> ambiguity again. The solution for this is described in [cmplx.over]
> p3 of C++11, where we have the following said:
>
> "Function template pow shall have additional overloads sufficient to
> ensure, for a call with at least one argument of type complex<T>:
>
> 1. If either argument has type complex<long double> or type long
> double, then both arguments are effectively cast to complex<long
> double>.
> 2. Otherwise, if either argument has type complex<double>, double,
> or an integer type, then both arguments are effectively cast to
> complex<double>.
> 3. Otherwise, if either argument has type complex<float> or float,
> then both arguments are effectively cast to complex<float>."

Great, I did not read this part. Given this list, is there any special
reason to list the three overloads of pow in [complex.syn]? I ask as
this is confusing because when I read that part, I thought those three
are all that offered as (it seems) in the case of c++03.

> The explicit template parameter does not solve the problem. In C++03
> it would still be ambiguous because of the three overloads
>
> template<> complex<float> pow(const complex<float>&, int);
> template<> complex<float> pow(const complex<float>&, const float&);
> template<> complex<float> pow(const complex<float>&, const
complex<float>&);
>
> In C++11 the code behaviour is unspecified, because from above
> wording in [cmplx.over] p3 there is an unspecified number of further
> functions or function templates implied. Don't write it that way in
> portable code.

Sure, after reading [cmplx.over].

> IMO this behaviour is incorrect. In C++03 this should be ambigious as
> explained above.

{ Quoted closing greetings and banner removed - mod}

Daniel Krügler

unread,
May 25, 2012, 4:20:44 AM5/25/12
to
On 2012-05-25 06:30, 萌 朱 wrote:
>> Let me nit-pick first that this program is already ill-formed
>> because there is no guarantee for the declaration of a function sqrt
>> being available. I'm adding an assumed
>>
>> #include<math.h>
>
> Thanks. Would it be better to #include<cmath> then?

Presumably yes, but I didn't do so, because in your original program you
were referring to sqrt, not std::sqrt, therefore the minimum necessary
change was to add <math.h>. If using <cmath>, you better refer to
std::sqrt, anything else would be non-portable.
Correct.

> (2) for
> template<class T> complex<T> pow(const complex<T>&, const T&);
> T is again deduced as float from c and as double from 1./2
> therefore, the deduction failed as T cannot be consistently
> deduced. so this one produces no functions.
> (3) for
> template<class T> complex<T> pow(const complex<T>&, const
> complex<T>&);
> T is deduced as float from c and failed for 1./2 because there is
> no type such that complex<type> would match double. therefore,
> deduction failed and this adds no function to the candidate set.
>
> Now overload resolution takes place, it sees only
> template<> complex<float> pow(const complex<float>&, int);
>
> and noticed that double has implicit conversion to int. This is to say
> this call is valid if only those four function templates are
> considered.
> I am learning template deduction rules and how they work in action.
> I appreciate if you can point me with detailed explanation where I was
> wrong in my logic. Thanks.

You are right, my explanation was incorrect. Some library
implementations did indeed provide overloads of the pow template instead
of the declared templates. In this case the call were ambiguous. If they
templates were declared, the selected overload should be

template<class T> complex<T> pow(const complex<T>&, int);

as you say. And the fact that some libraries provided those overloads is
non-conforming, but I think this was already done to realize the
additional overload requests from TR1. This should teach me that my own
prejudice is an easy trap for another incorrect conclusion chain - my
apologies for that.

>> Your deduction chain is wrong.

So were mine ;-)

> Great, I did not read this part. Given this list, is there any special
> reason to list the three overloads of pow in [complex.syn]? I ask as
> this is confusing because when I read that part, I thought those three
> are all that offered as (it seems) in the case of c++03.

I certainly agree that the editorial representation could be improved.
But I think that it is general the right decision to list the templates
at that point, because otherwise people might miss these functions from
the synopsis.

>> The explicit template parameter does not solve the problem. In C++03
>> it would still be ambiguous because of the three overloads
>>
>> template<> complex<float> pow(const complex<float>&, int);
>> template<> complex<float> pow(const complex<float>&, const float&);
>> template<> complex<float> pow(const complex<float>&, const
> complex<float>&);
>>
>> In C++11 the code behaviour is unspecified, because from above
>> wording in [cmplx.over] p3 there is an unspecified number of further
>> functions or function templates implied. Don't write it that way in
>> portable code.
>
> Sure, after reading [cmplx.over].
>
>> IMO this behaviour is incorrect. In C++03 this should be ambigious as
>> explained above.

Here I was wrong for the same reasons as mentioned above.

HTH & Greetings from Bremen,

Daniel Krügler


0 new messages