Question

i wrote a template class that should work for double and std::complex. as it is suppose to be, all my methods are in the .hpp file. all but one. i had to specialize a method because at some place i have to compute the square of a double or the norm of a std::complex. more explicitly for the "double specialization" (A):

double a(2.0);
double b(0.0);
b = a*a;

for the "complex specialization" (B):

std::complex<double> a(2.0,3.0);
double b(0.0);
b = std::norm(a);

my questions are :

  • is there a way to avoid theses specializations by using a function that works for both double and complex ? (because the std::norm works only for complex...)

  • or the only solution is to cast the double a of the specialization (A) into a complex and then use only the specialization (B) as a general template (working for both double and complex)?

Was it helpful?

Solution

You can minimise the divergent case by introducing your own function as a square/norm wrapper:

template <typename T>
double square_or_norm(T x);

template<>
inline double square_or_norm(double x) { return x * x; }

template <>
inline double square_or_norm(std::complex<double> x) { return norm(x); }

Then, use it inside the function which needs it:

template <typename T>
T computation(T x) {
  return some_code_with(x, square_or_norm(x));
}

OTHER TIPS

You could define two function template overloads (when it comes to function templates, overloading is usually preferable to specialization) called compute_norm(), one accepting std::complex and one accepting unconstrained types. The unconstrained template would invoke operator *, while the constrained template would invoke std::norm().

#include <complex>

template<typename T>
double compute_norm(T t)
{ return t * t; }

template<typename T>
double compute_norm(std::complex<T> const& t)
{ return std::norm(t); }

Then, your generic code that can work both with a double and with a complex<double> would call compute_norm():

#include <iostream>

template<typename T>
void foo(T&& t)
{
    // ...
    double n = compute_norm(std::forward<T>(t));
    std::cout << n << std::endl;
    // ...
}

For instance, the following program:

int main()
{
    double a(2.0);
    foo(a);

    std::complex<double> c(2.0, 3.0);
    foo(c);
}

Will output:

4
13

Here is a live example.

If you have a conforming standard library, there is an overload of std::norm for floating point types:

26.4.9 Additional overloads [cmplx.over]
The following function templates shall have additional overloads: arg norm conj proj imag real
The additional overloads shall be sufficient to ensure:

  1. If the argument has type long double, then it is effectively cast to complex.
  2. Otherwise, if the argument has type double or an integer type, then it is effectively cast to complex< double>.
  3. Otherwise, if the argument has type float, then it is effectively cast to complex.

This should work (and does on gcc 4.7.2)

#include <complex>
#include <iostream>

int main()
{
    std::complex<double> c {1.5, -2.0};
    double d = 2.5;

    std::cout << "|c| = " << std::norm(c) << '\n'
              << "|d| = " << std::norm(d) << '\n';
}

Why not just use function overloading?

double myNorm(double);
double myNorm(std::complex<double>);


double myNorm(double x) {
    return x * x;
}
double myNorm(std::complex<double> x) {
    return std::norm(x);
}

You can put the implementation in your .cpp or (when inlined) in your header file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top