首页 文章

从类类型到类类的隐式转换

提问于
浏览
8

我正在研究在C中转换构造函数和转换运算符 . 到目前为止我学到的是,任何只带一个参数(以及任意数量的可选默认参数)的非显式构造函数表示对THAT类类型的隐式类类型转换,例如,如果类定义了一个构造函数,一个 int 类型的参数我可以在需要该类类型的对象的地方使用 int

(假设class_type有一个重载的=运算符)

class_type a;
a+=5;

在这种情况下,5被隐式转换(通过转换构造函数)到 class_type ,并调用重载的运算符 .

现在,(至少对我来说)棘手的部分:我知道我可以将转换运算符定义为成员函数:

operator int() {....};

class_type 的对象转换为原始 int 类型,我可以使用该转换,如:

class_type a;
a+5;

在这种情况下,我已经读过对象通过其转换运算符转换为int,然后调用内置和运算符 . 但是如果我定义了一个重载运算符来将两个class_type对象作为其参数呢?就像是

class_type operator+(const class_type&,const class_type &c);

编译器如何知道通过函数匹配调用哪一个?转换为 int 只会在仅定义内置运算符时隐式发生?

谢谢!

编辑:

实际上,我已经尝试编写一些代码来有效地尝试它,结果发现我的编译器(g)没有发出任何模糊的调用错误!

这是类头(以及非memeber操作符函数声明):

#include <iostream>

class wrapper {
    friend std::ostream &operator<<(std::ostream&,const wrapper&);
  public:
    wrapper()=default; 
    wrapper(int);
    int get();
    operator int() const;
    wrapper operator+(int);
  private:
    int a=10;
};

std::ostream &operator<<(std::ostream&,const wrapper&);

这是主要代码:

#include "wrapper.h"

int main()
{
  using namespace std;
  wrapper w1;
  wrapper w2(5);
  cout<<w1<<" "<<w2<<endl;
  w1+1;  
}

现在,我已经定义了从 intwrapper 的转换构造函数和从类类型到 int 的转换运算符(我还重载了<< output运算符以便打印一些结果),但是当编译器计算表达式 w1+1 时似乎很好 . 怎么可能?

2 回答

  • 3

    如果您有以下类声明,其中包含转换构造函数和转换运算符

    struct A
    {
        A( int x ) : x( x ) {}
        operator int() const { return x; }
        int x;
    };        
    
    const A operator +( const A &a1, const A &a2 )
    {
        return A( a1.x + a2.x );
    }
    

    声明

    a1 + a2;
    

    其中a1和a2被声明为例如

    A a1( 10 );
    A a2( 20 );
    

    因为不需要调用转换函数,所以格式正确 . 两个操作数都匹配运算符的参数声明 .

    但是,如果你要写的话

    a1 + 20;
    

    当编译器发出错误时,因为存在歧义 . 编译器可以应用转换构造函数 A( int ) 将第二个操作数转换为类型 A ,并调用为 A 类型的对象定义的运算符 . 或者它可以应用转换运算符 operator int 将第一个操作数转换为类型 int ,并为 int 类型的对象调用内置 operator + .

    为避免这种歧义,您可以使用函数说明符 explicit 声明构造函数或运算符(或两者) .

    例如

    explicit A( int x ) : x( x ) {}
    

    要么

    explicit operator int() const { return x; }
    

    在这种情况下,只存在一个隐式转换,并且没有任何限制 .

    我想附加上面的描述,有时一些转换运算符可以隐式调用,即使它们是用函数说明符 explicit 声明的 .

    例如根据C标准(6.4选择声明)

    ...作为表达式的条件的值是表达式的值,对于除switch之外的语句,上下文转换为bool;

    和(5.16条件运算符)

    1条件表达式从右到左分组 . 第一个表达式在上下文中转换为bool(第4条) .

    因此,例如,如果上面的类具有使用函数说明符 explicit 声明的以下转换运算符

    explicit operator bool() const { return x != 0; }
    

    然而,它将被隐含地称为例如以下声明中

    A a( 10 );
    
    std::cout << ( a ? "true" : "false" ) << std::endl;
    

    这里a将在条件运算符中转换为bool类型的对象 .

    编辑:更新您的问题后,这个表达式

    w1+1;
    

    与操作员完全匹配

    wrapper operator+(int);
    

    无需转换 . 所以代码编译成功 .

  • 15

    您可以轻松尝试查看编译器的功能:

    #include <iostream>
    
    struct ABC {
        int v;
        ABC(int x) : v(x) { }
        operator int() const { return v; }
        void operator +=(ABC const &that) {
            v += that.v;
        }
    };
    
    ABC operator+(ABC const &lhs, ABC const &rhs) {
        return { lhs.v + rhs.v };
    }
    
    int main() {
        ABC a(5);
        std::cout << a + 1 << '\n';
        a += 10;
        std::cout << a << '\n';
    }
    

    如果我定义了一个重载运算符以将两个class_type对象作为其参数,该怎么办?

    GCC

    错误:'operator'的模糊重载(操作数类型是'ABC'和'int')

    编译器会看到两个候选项: operator+(int, int) <built-in>ABC operator+(const ABC&, const ABC&) . 这意味着它不仅可以隐式地将 a + 5 中的 5 转换为 a 而且将 a 隐式转换为 int . 发布这些转换后, operator+ 函数都会成为潜在的匹配项 .

    编译器如何知道通过函数匹配调用哪一个?

    因此不知道错误 .

    只有在定义了内置运算符时,才会隐式转换为int吗?

    是的,否则它不会自动将 class_type 转换为 int . 但是, intclass_type 会隐式发生,除非你做 class_type 的构造函数 explicit

    explicit ABC(int x) : v(x) { }
    

    如果您可以访问C 11,那么您还可以明确转换函数:

    explicit operator int() const { return v; }
    

相关问题