2

valarray<double> can be multiplied by a scalar. However, I get an error when I want to multiply by a scalar for a valarray<complex<double>>. I'm wondering if there's a way to do it in a pretty manner. Here's a reproduction of the problem:

valarray<complex<double>> v1{ { complex<double>{1,0}, complex<double>{2,0}, complex<double>{3,0} } };
valarray<complex<double>> v2 = v1 * 2.0; // error

generates Error C2784: 'std::complex<_Other> std::operator *(const std::complex<_Other> &,const std::complex<_Other> &)': could not deduce template argument for 'const std::complex<_Other> &' from 'std::vector<std::complex<double>,std::allocator<_Ty>>'.

So I tried to build my own function to see if it can be done and the following works:

valarray<complex<double>> VAMult(const valarray<complex<double>> &v, double scalar)
{
    valarray<complex<double>> out(v.size());
    for (size_t i = 0; i < v.size(); i++)
    {
        out[i] = v[i] * scalar;
    }
    return out;
}

// makes the following code work:
valarray<complex<double>> v2 = VAMult(v1, 2.0);

But this implementation would make for really ugly code so I looked into valarray.h and found the * overload definition:

operator*(const _Ty& _Left,
        const valarray<_Ty>& _Right)
{   // return scalar * valarray
    _VALOP(_Ty, _Right.size(), _Left * _Right[_Idx]);
}

#define _VALOP(TYPE, LENGTH, RHS)   /* assign RHS(_Idx) to new valarray */ \
valarray<TYPE> _Ans(LENGTH); \
for (size_t _Idx = 0; _Idx < _Ans.size(); ++_Idx) \
    _Ans[_Idx] = RHS; \
return (_Ans)

My knowledge of templating is very limited but is it possible to extend this class? Here's my attempt:

valarray<complex<double>> valarray<complex<double>>::operator*(const double &scalar)
{
    return valarray<complex<double>>{};
}

resulting in Error C2039 '*': is not a member of 'std::valarray<std::complex<double>>'

Is there a way to make it so my first line of code v1 * 2.0 works or some close compromise?

max66
  • 65,235
  • 10
  • 71
  • 111
Phlox Midas
  • 4,093
  • 4
  • 35
  • 56

2 Answers2

3

Is there a way to make it so my first line of code v1 * 2.0 works or some close compromise?

Yes: multiply v1 with a complex scalar.

The following code compile

#include <complex>
#include <valarray>

int main()
 {
   std::valarray<std::complex<double>> v1{ { {1,0}, {2,0}, {3,0} } };
   std::valarray<std::complex<double>> v2 = v1 * std::complex<double>{2.0};
 }

The problem is that valarray is a template class with an operator*() (and similar operators) that is defined only over the template type.

So a std::valarray<double> can be multiplied with a double scalar, a std::valarray<std::complex<double>> can be multiplied with a std::complex<double> scalar but a std::valarray<std::complex<double>> can't be multiplied with a double scalar.

max66
  • 65,235
  • 10
  • 71
  • 111
  • I could have sworn I tried this but the second and third values were random. This is an acceptable compromise given my limited scope. – Phlox Midas Sep 15 '16 at 17:20
3

Specializing operator* in this case is both illegal and won't get you what you want.

An overload could, but it is illegal to add overloads to namespace std. (This may change in or )

We can do:

valarray<complex<double>> v1{ { complex<double>{1,0}, complex<double>{2,0}, complex<double>{3,0} } };
valarray<complex<double>> v2 = v1 * complex<double>{2.0};

but I assume you knew that.

You could use named operators and get:

valarray<complex<double>> v2 = v1 *times* 2.0;

to work legally.

You could also write your own literal to-complex converter:

std::complex<double> operator"" _cplx( long double t ) {
  return {(double)std::move(t)};
}

Live example.

But really, cast to complex<double>.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524