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