Re: Implicit conversion to complex<double>
perroe@nr.no wrote:
Hi
I have a array of complex numbers that are stored in a simple double
array. This is done since the array is part of an wrapper for an
external C library, and the imaginary part of the first element, and
the last element are known to be 0. I've implemented a []-operator
that returns a ComplexReference object that basically maps a
complex<double> into the storage used in the array.
What I would like to do is using the ComplexReference object as a
complex<double> object, as shown in the code below, but I can not do
this implicitly, only through the explicit cast.
Is there any way to make this work so that the ComplexReference object
will behave like a complex<double> from the users point of view
without wrapping all the functions associated with std::complex, using
a temporary complex<double> object, or using a explicit cast?
Not really. One class can never behave just like another. For example, only
one user defined conversion may be called implicitly to match a function
argument. The problem with the functions associated with std::complex are
that they are templates. If you take std::abs, you have
double abs(double);
float abs(float);
and so on in <cmath>,
and the function you are interested in, is
template <class T> T abs(const complex<T>&);
And as you have seen, the compiler will not even try this. The problem is
that T cannot be deduced.
#include "complexarray.h"
#include <complex>
#include <iostream>
int main()
{
ComplexArray a(6);
// Works:
a[0] = std::complex<double>(1.0, 0.0);
a[1] = std::complex<double>(5.1, 2.0);
a[2] = std::complex<double>(2.0, 1.1);
a[3] = std::complex<double>(5.5, 0.0);
// Does not work:
std::cout << "|a[2]| = " << std::abs(a[2]) << "\n";
// Works
std::cout << "|a[2]| = " <<
std::abs(static_cast<std::complex<double> >(a[2])) << "\n";
This works because both the argument for abs and its expected parameter is
on the form
std::complex<T>,
which means T can be deduced.
// Also works
std::complex<double> c = a[2];
std::cout << "|a[2]| = " << std::abs(c) << "\n";
//Also works:
std::cout << std::abs<double>(c);
Here, there are only one candidate, and the template parameter is selected
by hand.
//Also works
std::cout << abs(std::complex<double>(c));
This is the cast with function style syntax. At least it is shorter than
static_cast. Note that since the argument is a class from std, the compiler
will look for abs in std.
exit(EXIT_SUCCESS);
EXIT_SUCCESS is declared in <cstdlib>/<stdlib.h>
}
complexarray.h:
#include <complex>
#include <vector>
class ComplexReference {
public:
ComplexReference(double& re, double& im)
: re_(re), im_(im) {}
inline operator std::complex<double>() const
Note that inline is implied for functions defined inside the class
definition.
{
return std::complex<double>(re_, im_);
}
inline ComplexReference&
operator=(const std::complex<double> c)
{
re_ = c.real();
im_ = c.imag();
return *this;
}
private:
double& re_;
double& im_;
};
class ComplexArray {
public:
typedef ComplexReference reference;
typedef std::complex<double> const_reference;
// Note: n is length of data, not number of complex elements.
explicit ComplexArray(int n = 0) : data_(n), dummy(0) {}
inline const_reference operator[](size_t i) const
size_t should be a typedef. It is defined in <cstddef>, <cstdio>, <cstring>
and <ctime>, and unless you include one of the .h variants, you should use
std::size_t, or write using std::size_t. Here, however, the correct type
would be std::vector<double>::size_type.
{
size_t n = data_.size();
if (i == 0 || (i == n/2 && n%2 == 0)) {
return const_reference(data_[i], 0);
}
return const_reference(data_[i], data_[data_.size() - i]);
}
inline reference operator[](size_t i)
{
size_t n = data_.size();
if (i == 0 || (i == n/2 && n%2 == 0)) {
return reference(data_[i], dummy);
}
return reference(data_[i], data_[data_.size() - i]);
}
inline size_t size() const
{
return data_.size() / 2 + 1;
}
private:
/// The data vector.
std::vector<double> data_;
/// Dummy object.
double dummy;
};
--
rbh