强制类型转换运算符始终存在歧义

3

考虑以下示例:

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
}

我调用强制转换时失败了:从qm的转换不明确。为什么会这样?我明确添加了m的强制转换操作符!为什么还会模棱两可?然而,如果我删除对double的转换,它就可以正常工作。为什么会有这个影响?我该如何做才能实现我的目的?


如果我删除将其转换为double的强制转换,它也可以工作,但这反过来意味着当我只想转换为double时会失去性能。 - Matteo Monti
mpf_class 是如何定义的?它是什么? - user35443
mpf_class是GMP(Gnu Multiple Precision)库中的主要类之一。 - Matteo Monti
你尝试在运算符之前使用“explicit”了吗? - user35443
2个回答

2

考虑在执行以下操作时编译器可能采取的路径:

m temp__(x);

有三个相关的构造函数:

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

我们可以通过 x --> double --> int 来调用 (A),这是一个用户定义的转换序列。
我们也可以通过 x --> m 来调用 (B)(C),这也是一个用户定义的转换序列。
只有当用户定义的转换序列最终调用相同的构造函数时,它才能优先于另一个。但在这种情况下,它们没有调用相同的构造函数,因此编译器无法优先选择其中之一。因此,出现了二义性。
您的替代方案可以是:
  • 直接调用您的运算符(或将其命名为某个函数);或
  • 使 q 继承自 m - 派生到基类的转换将优先于用户定义的转换序列;或
  • 使两个转换运算符都是 explicit - 在这种情况下,只有一个可行的候选者,因此不会出现二义性。

0

当我使用g++ 5.1编译您的程序时,我得到以下结果:

/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)
   ^

问题转换表达式 (mpf_class) x 需要从 q 构造一个 mpf_class (typedef __gmp_expr<mpf_t, mpf_t> mpf_class)。因此,您可以考虑
q x(1);
mpf_class m(x);

这是什么引起了相同的诊断结果。

为什么构造函数不明确?因为:

  • 当然,不存在名为mpf_class(q const &)的构造函数
  • 因此,如果存在从q到在诊断中枚举的11种其他类型之一的转换,则需要进行转换,以便可以构造出一个mpf_class
  • 但是,由于您提供了两个强制转换运算符,因此有两种这样的转换方法,每种都很好。

转换operator double()可以满足11个构造函数中的第一个,而operator mpf_class()可以满足最后一个。编译器没有选择任何一个构造函数的依据。

如果您牺牲其中一个强制转换运算符,问题将会消失。如果您必须同时拥有它们两个,则可以通过使它们两个都成为explicit来解决问题,这样编译器就不会考虑调用转换,除非它被显式调用:

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

然后你会发现,例如:

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;
}

顺便提一下:

  • 强制类型转换操作符应该是const
  • 在函数内部,当存在隐式转换时,将返回值强制转换为返回类型是多余的。

因此:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接