Making one user defined conversion function preferred over another
Asked Answered
C

1

6

I am trying to write a simple class that will act as a complex<double> number when used in a context that warrants a complex number, otherwise it will act as a double (with the requirement that the imaginary part must be 0 when used this way)

This is what I have so far:

class Sample
{
public:
    Sample() {}
    Sample(double real) : m_data(real) {}
    Sample(double real, double imaginary) : m_data(real, imaginary) {}

    operator std::complex<double>() const
    {
        return m_data;
    }

    operator double() const
    {
        assert(m_data.imag() == 0.0);
        return m_data.real();
    }

private:
    std::complex<double> m_data;
};

This works great for most situations. Anytime this class is passed to a function that expects a complex number it will act as though it is a complex number. Anytime it is passed to a function that expects a double it will act as though it is a double.

The problem arises when I pass it to a function that will accept BOTH complex numbers and doubles. (for example std::arg). When I try to pass a Sample object to std::arg it doesn't know which conversion to use since both are technically valid.

In situations like this I want the complex conversion to be "preferred" and have it just pass it as a complex. Is there any way to make one user defined function preferred over another when both conversions would be technically acceptable?

Careycarfare answered 14/10, 2020 at 2:54 Comment(0)
L
0

I think in general it is impossible to write one conversion operator that will always be preferred to another conversion operator in the same class.

But if you need just to call std::arg with the argument of your class, then it already behaves as you expect preferring std::complex<double> over double:

#include <complex>
#include <iostream>

struct S {
    operator std::complex<double>() const { 
        std::cout << "std::complex<double>";
        return 2; 
    }
    operator double() const { 
        std::cout << "double";
        return 2; 
    }
};

int main() {
    S s;
    //(void)std::arg(s); //error: cannot deduce function template parameter
    (void)std::arg<double>(s);
}

The program compiles and prints std::complex<double>. Demo: https://gcc.godbolt.org/z/dvxv17vvz

Please note that the call std::arg(s) is impossible because template parameter of std::complex<T> cannot be deduced from s. And std::arg<double>(s) prefers std::complex overload in all tested implementations of the standard library.

Linden answered 6/11, 2021 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.