The casting operator is always ambiguous

Consider the following example:

struct m
{
    m(int ) { }
};

class q
{
public:

    int i;

    q(int i)
    {
        this->i = i;
    }

    operator double()
    {
        return (double) i;
    }

    operator m()
    {
        return (m)i;
    }
};

int main()
{
    q x(1);
    (m)x; // error: ambiguous
}

      

It fails when I call casting: ambiguous conversion from q

to m

. Why is that? I really do not understand. I have explicitly added a casting operator for m

! Why should this be ambiguous? However, if I remove the broadcast on double

, it works. Why does it matter? How can I do what I want?

+3


source to share


2 answers


Consider the possible paths that the compiler can take when you do:

m temp__(x);

      

There are three corresponding constructors:

m(int );      // (A)
m(const m& ); // (B)
m(m&& );      // (C)

      

We could call (A)

through x --> double --> int

, that is, a custom transformation sequence.



We could name (B)

or (C)

via x --> m

, this is also a custom transformation sequence.

A custom conversion sequence may only be preferable to the other if they end up calling the same constructor. But in this case they do not, so the compiler cannot prefer each other. Hence, ambiguous.

Your alternatives would be

  • call the operator directly (or make it a function with some name); or
  • make q

    inherit from m

    - a conversion with a derived base would be preferable to a user-defined sequence of conversions; or
  • do both conversion operators explicit

    - in this case, there would be only one viable candidate from the start, so there would be no ambiguity.
+2


source


When I compile your program with g ++ 5.1 I get:

/home/imk/develop/so/mpf_cast/main.cpp:35:25: error: call of overloaded ‘__gmp_expr(q&)’ is ambiguous
     cout << (mpf_class) x << endl;
                         ^
/home/imk/develop/so/mpf_cast/main.cpp:35:25: note: candidates are:
In file included from /home/imk/develop/so/mpf_cast/main.cpp:2:0:
/usr/include/gmpxx.h:1883:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(double)
   __gmp_expr(double d) { mpf_init_set_d(mp, d); }
   ^
/usr/include/gmpxx.h:1880:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(float)
   __gmp_expr(float f) { mpf_init_set_d(mp, f); }
   ^
/usr/include/gmpxx.h:1876:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(long unsigned int)
   __gmp_expr(unsigned long int l) { mpf_init_set_ui(mp, l); }
   ^
/usr/include/gmpxx.h:1873:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(long int)
   __gmp_expr(signed long int l) { mpf_init_set_si(mp, l); }
   ^
/usr/include/gmpxx.h:1869:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(short unsigned int)
   __gmp_expr(unsigned short int s) { mpf_init_set_ui(mp, s); }
   ^
/usr/include/gmpxx.h:1866:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(short int)
   __gmp_expr(signed short int s) { mpf_init_set_si(mp, s); }
   ^
/usr/include/gmpxx.h:1862:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(unsigned int)
   __gmp_expr(unsigned int i) { mpf_init_set_ui(mp, i); }
   ^
/usr/include/gmpxx.h:1859:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(int)
   __gmp_expr(signed int i) { mpf_init_set_si(mp, i); }
   ^
/usr/include/gmpxx.h:1855:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(unsigned char)
   __gmp_expr(unsigned char c) { mpf_init_set_ui(mp, c); }
   ^
/usr/include/gmpxx.h:1852:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(signed char)
   __gmp_expr(signed char c) { mpf_init_set_si(mp, c); }
   ^
/usr/include/gmpxx.h:1837:3: note: __gmp_expr<__mpf_struct [1], __mpf_struct [1]>::__gmp_expr(const __gmp_expr<__mpf_struct [1], __mpf_struct [1]>&)
   __gmp_expr(const __gmp_expr &f)
   ^

      

To express the problem (mpf_class) x

requires mpf_class

( typedef __gmp_expr<mpf_t, mpf_t> mpf_class

) we construct from a q

. So you can just consider

q x(1);
mpf_class m(x);

      

which causes the same diagnosis.

Why is construction controversial? Because: -

  • Of course, there is no such constructor as mpf_class(q const &)

  • Consequently, a conversion, if any, is required from q

    to one of the 11 other types listed in the diagnosis from which to build mpf_class

    .
  • But there are two such conversions, each better than the other, thanks to the two broadcast operators you provided with q

    .

The transformation operator double()

can satisfy the first of the 11 constructors and operator mpf_class()

can satisfy the last. The compiler has no reason to prefer any of these constructors.



The problem goes away if you donate one or another of your broadcast operators. If you must have both of them, then you can also solve this by doing both of them explicit

, so that the compiler will not consider calling the conversion unless it is called by explication:

explicit operator double()
{
    return (double)i;
}
explicit operator mpf_class()
{
    return (mpf_class)i;
}

      

Then you will find for example:

int main()
{
    q x(1);
    mpf_class mpf(x); // via `explicit operator mpf_class()`
    double d(x); // via `explicit operator double()`
    // d = q(2); <- does not compile
    d = static_cast<double>(q(2));
    // mpf = q(2); <- does not compile
    mpf = static_cast<mpf_class>(q(2));
    return 0;
}

      

By the way:

  • Broadcast operators must be const

  • Inside a superfield function, cast the return value into the return type when an implicit conversion exists.

Hence:

explicit operator double() const
{
    return i;
}
explicit operator mpf_class() const
{
    return i;
}

      

0


source







All Articles