【问题标题】:Implicit VS Explicit Conversion隐式 VS 显式转换
【发布时间】:2011-10-29 07:43:09
【问题描述】:

Nicolai M. Josuttis 的 C++ 标准库指出:

两者之间存在细微差别

X x;
Y y(x) //explicit conversion

X x;
Y y = x; //implicit conversion

下面说:“前者通过从X类型的显式转换创建一个Y类型的新对象,而后者通过使用隐式转换创建一个Y类型的新对象。”

我猜我对显式转换和隐式转换的概念有点困惑。在这两种情况下,您都使用 X 并将其推入 Y 本身 - 一种使用 Y 的构造函数,而另一种使用赋值运算符。

在这两种情况下如何处理转换有什么区别,是什么使它显式/隐式,以及这与使用“显式”关键字定义的类构造函数(如果有的话)有什么关系?

【问题讨论】:

标签: c++ type-conversion


【解决方案1】:

隐式转换不需要任何转换操作符。当将数据从较小的整数类型转换为较大的类型或派生类型转换为基类型时,通常会使用这种转换。

int iVal = 100; 双 dVal = iVal;

显式转换构造函数优于隐式转换运算符,因为在后一种情况下,需要额外调用复制构造函数。Implicit and Explicit converstions

【讨论】:

    【解决方案2】:

    一个使用 Y 的构造函数,一个使用赋值运算符。

    不。在第二种情况下,它不是一个赋值,它是一个初始化,赋值运算符 (operator=) 永远不会被调用;而是调用非explicit 单参数构造函数(接受X 类型作为参数)。

    初始化和赋值之间的区别很重要:在第一种情况下,正在创建一个新对象,它以初始化它的值开始它的生命(因此调用构造函数),而赋值发生当一个对象被分配(~复制)给一个已经存在并且已经处于确定状态的对象时。

    无论如何,您编写的两种初始化形式的不同之处在于,在第一种情况下,您显式调用构造函数,因此任何构造函数都是可以接受的;在第二种情况下,您隐式调用了构造函数,因为您使用的不是“经典”构造函数语法,而是初始化语法。

    在这种情况下,只接受未标记explicit 的单参数构造函数。这样的构造函数被某些人称为“转换”构造函数,因为它们涉及隐式转换。

    根据in this other answer 的规定,任何未标记为explicit 的构造函数都可以参与隐式转换,例如将传递给函数的对象转换为该函数所期望的类型。实际上,您可能会说这就是您的第二个示例中发生的情况:您想用x 初始化(=使用从其他地方复制的值创建)y,但首先必须将x 转换为类型Y ,这是通过隐式构造函数完成的。

    这种隐式转换通常是可取的:例如考虑到一个字符串类,它具有来自const char * 的转换(即非explicit)构造函数:任何接收string 参数的函数也可以是使用“普通”C 字符串调用:由于转换构造函数,调用者将使用 C 字符串,被调用者将收到其 string 对象。

    不过,在某些情况下,单参数构造函数可能不适合转换:通常,当它们的唯一参数在概念上没有“转换”为正在创建的对象的类型时,通常会发生这种情况,但它只是构造的一个参数;例如考虑一个文件流对象:可能它会有一个构造函数来接受要打开的文件的名称,但是说这样的字符串被“转换”为适用于该文件的流是没有意义的。

    您还可以找到一些更复杂的场景,在这些场景中,这些隐式转换会完全破坏程序员对重载解析所期望的行为;这方面的例子可以在我上面链接的答案下面找到。

    更简单地说,某些构造函数可能非常重量级,因此类设计者可能希望确保显式调用它们。在这些情况下,构造函数被标记为explicit,因此它只能在“显式作为构造函数”调用时使用,并且不参与隐式转换。

    【讨论】:

      【解决方案3】:

      第一种形式是直接初始化。二是复制初始化

      复制初始化隐式调用转换构造函数或转换运算符,然后显式调用复制构造函数(可以省略复制构造函数调用,但仍必须执行可访问性检查)。

      考虑第三种可能性,即复制初始化,但转换是显式的:

      Y y = Y(x);
      

      Y y = (Y)x;
      

      【讨论】:

      • 你能提供更多细节吗?当你说隐式调用转换构造函数时,你的意思是像 STL 这样的模板构造函数将用来提供隐式类型转换吗?又是什么使它隐含/明确-您在答案中使用了使我感到困惑的术语。不过谢谢你,我看到你的思路是对的,我只是需要稍微降低一点。
      • @w00te:隐式转换是编译器为了匹配类型而添加的转换。使用强制转换语法时存在显式转换(包括功能强制转换,它看起来像构造函数调用)。在第一种情况下,你有一个显式的构造函数调用(参数在括号内,它看起来像一个函数调用)。在第二种情况下,复制构造函数调用被认为是显式的,但需要将参数强制转换为正确的类型,并且由于您没有在源代码中进行强制转换,因此该强制是隐式的。
      • 简单:明确表示您请求转换。隐式意味着您没有请求转换,它被添加是因为您的代码要求执行其他操作。
      • 感谢您提供的额外详细信息,这让您明白了很多。由于他们提供了帮助,因此提高了对评论/解决方案的投票。 :)
      【解决方案4】:

      一个使用赋值运算符

      不,它没有。它直接调用构造函数。

      一个是显式的,一个是隐式的的原因是,当你不希望它们发生时,可能会发生隐式转换。明确的不能。最简单的例子是 bool。

      假设你发明了某种类型,它可以是真也可以是假——比如指针。然后让我们进一步说,您决定为了让用户的生活更轻松,您让它隐式转换为 bool。这很好——直到你的一个用户做了一些愚蠢的事情。

      int i = 0;
      i = i >> MyUDT();
      

      哦,等等-为什么它甚至可以编译?您根本无法转移 MyUDT!它编译是因为bool 是一个整数类型。编译器将其隐式转换为布尔值,然后转换为可以移动的东西。上面的代码显然是愚蠢的——我们只希望人们能够转换为 bool,而不是 bool 以及 bool 可能想要做的任何其他事情。

      这就是 C++0x 中添加显式转换运算符的原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-11-15
        • 2018-06-17
        • 2019-04-05
        • 2011-07-01
        • 1970-01-01
        • 2016-07-15
        • 1970-01-01
        相关资源
        最近更新 更多