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

implicit conversions from and to class types

本文关键字:类型 转换      更新时间:2023-10-16

我正在学习c++中的转换构造函数和转换操作符。到目前为止,我学到的是,任何只接受一个参数(和任意数量的可选默认参数)的非显式构造函数都表示隐式的类类型转换为该类类型,例如,如果一个类定义了一个构造函数,具有一个类型为int的参数,我可以在需要该类类型的对象时使用int:

(假设class_type有重载的+=操作符)

class_type a;
a+=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++)不会发出任何模糊的调用错误!

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

#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的转换操作符(我还重载了<<输出操作符,以便打印一些结果),但是当编译器计算表达式w1+1时,它似乎没有问题。怎么可能呢?

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

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选择语句)
  • …方法的值作为条件的值表达式,在上下文中为语句other转换为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);
    

    两种转换都不需要。因此代码编译成功。

    你可以很容易地尝试一下,看看编译器是怎么做的:

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