【问题标题】:implicit conversions from and to class types与类类型之间的隐式转换
【发布时间】:2015-11-11 03:15:48
【问题描述】:

我正在研究 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++) 没有发出任何模棱两可的调用错误!

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

#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 时,它似乎没问题。怎么可能?

【问题讨论】:

  • 也许阅读this implicit conversions reference 会有所帮助?
  • 另外,对于a+=5 示例,我假设您的意思是您以class_type 为参数重载了operator+=?因为它很容易成为一个过载int
  • 根据经验,同时使用隐式构造函数和隐式转换运算符不仅会混淆编译器,还会混淆人类读者。

标签: c++ c++11 type-conversion


【解决方案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 选择语句)

  1. ...作为表达式的条件的值是 表达式,上下文转换为布尔语句其他 比切换;

和(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);

不需要转换。所以代码编译成功。

【讨论】:

  • 赞成记住 explicit operator bool 将被隐式调用的特殊情况,或标准调用的 contextually converted
  • @legends2k 你是一个善良的人!:你能不能也给我的其他答案投票?:)
  • @VladfromMoscow 你能看看我的编辑吗?非常感谢!
  • @Luca 查看我更新的帖子。考虑到在回答问题后不要更改问题是个好主意。否则他们会迷惑读者。
【解决方案2】:

这是您可以轻松尝试并查看编译器所做的事情:

#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 对象作为它的参数呢?

海合会

错误:“operator+”的重载不明确(操作数类型为“ABC”和“int”)

编译器看到两个候选者:operator+(int, int) &lt;built-in&gt;ABC operator+(const ABC&amp;, const ABC&amp;)。这意味着它不仅可以将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; }

【讨论】:

    猜你喜欢
    • 2012-01-21
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-16
    • 2013-07-09
    • 1970-01-01
    相关资源
    最近更新 更多