I want my custom complex type to be able to interact with std::complex, but in certain cases, the compiler do not convert my type to std::complex.
Here is a minimal working example:
#include <complex>
#include <iostream>
template <typename Expr>
class CpxScalarExpression
{
public:
inline std::complex< double > eval() const { return static_cast<Expr const&>(*this).eval(); }
inline operator std::complex< double >() const { return static_cast<Expr const&>(*this).eval(); }
};
class CpxScalar : public CpxScalarExpression<CpxScalar>
{
public:
CpxScalar() : m_value(0) {}
CpxScalar(const double value) : m_value(value) {}
CpxScalar(const double real_value, const double imag_value) : m_value(real_value, imag_value) {}
CpxScalar(const std::complex< double > value) : m_value(value) {}
template<typename Expr>
CpxScalar(const CpxScalarExpression< Expr >& expr) : m_value(expr.eval()) {}
public:
inline std::complex< double > eval() const { return m_value; }
private:
std::complex< double > m_value;
};
int main()
{
CpxScalar a(10,-5);
//std::complex< double >* b = reinterpret_cast< std::complex< double >* >(&a);
std::complex< double > b = a;
b = a;
//std::cout << b->real() << " " << b->imag();
std::cout << b.real() << " " << b.imag();
}
The compiler fails at deducing which operator = to call and returns the following error
est.cpp:50:4: error: no match for ‘operator =’ (operand types are ‘std::complex<double>’ and ‘CpxScalar’)
50 | b = a;
| ~~^~~~
In file included from test.cpp:1:
/usr/include/c /9/complex:1287:7: note: candidate: ‘std::complex<double>& std::complex<double>::operator =(double)’
1287 | operator =(double __d)
| ^~~~~~~~
/usr/include/c /9/complex:1287:25: note: no known conversion for argument 1 from ‘CpxScalar’ to ‘double’
1287 | operator =(double __d)
| ~~~~~~~^~~
/usr/include/c /9/complex:1329:9: note: candidate: ‘template<class _Tp> std::complex<double>& std::complex<double>::operator =(const std::complex<_Tp>&)’
1329 | operator =(const complex<_Tp>& __z)
| ^~~~~~~~
/usr/include/c /9/complex:1329:9: note: template argument deduction/substitution failed:
test.cpp:50:7: note: ‘CpxScalar’ is not derived from ‘const std::complex<_Tp>’
50 | b = a;
| ^
Is there a way to overcome this issue ?
CodePudding user response:
Providing your own overload for the operator is the way to go.
However, there's a few things to keep in mind:
Since you already have a cast available, all you have to do is to use it and let the regular operator take it from there.
Since the type that is meant to "pose" as
std::complex
isCpxScalarExpression<Expr>
, then that should be the one the overload operates on.std::complex
'soperator =()
normally allows you to add together complex values of different types, so we should maintain that. Meaning the operator's should be templated on the incomingstd::complex
's components type.We need to make sure to return exactly whatever
std::complex
'soperator =
wants to return. Usingdecltype(auto)
as the return type of the overload provides you with just that.
Putting all of that together, we land at:
template<typename T, typename Expr>
constexpr decltype(auto) operator =(
std::complex<T>& lhs,
const CpxScalarExpression<Expr>& rhs) {
return lhs = std::complex<double>(rhs);
}
Dropping that into the code you posted makes it work just as expected, and should give you feature parity with std::complex
's operator =()
.
CodePudding user response:
A custom operator for =
works with your example:
[[maybe_unused]] constexpr static inline std::complex<double> operator =(
const std::complex<double>& lhs,
const CpxScalar& rhs) noexcept {
return lhs rhs.eval();
}