【问题标题】:How is the initialization below well-formed?下面的初始化格式如何?
【发布时间】:2016-08-23 19:58:58
【问题描述】:

[class.conv.ctor]/2 中的示例包含以下初始化:

Z a3 = Z(1);    // OK: direct initialization syntax used

这如何被视为直接初始化语法?

【问题讨论】:

  • 您在标题中提出了一个问题,而在正文中提出了另一个问题。你应该问一个明确的问题。
  • 你为什么这么坚持不澄清问题本身?您认为未来的读者是否希望通过所有 cmets 来了解您的“问题”?此外,您的“解释”充满了错误的前提。 a3 的初始化不是直接初始化这一事实不会使代码格式错误,正如已经向您解释过无数次的那样。
  • @WakeupBrazil 已经在两个答案和一些 cmets 中反复解释。真不知道还能加什么。
  • 该短语中的“唯一”指的是使用显式构造函数初始化值的子表达式。 not 是说 only 直接初始化可以在使用显式构造函数的 full-expression 中使用。使用显式构造函数的子表达式是Z(1)。在包含它的完整表达式中使用显式构造函数的此子表达式的存在,Z a3 = Z(1); 并不禁止在完整表达式的其他部分中进行其他类型的构造。 ...
  • Z1 a3 = 部分是完整表达式的不同部分。它不使用显式构造函数。它使用显式构造函数的result,这不是一回事。就像Z b(1); Z c = b; 在构造c 时不使用显式构造函数一样,表达式Z a3 = x; where X is anything 导致Z 不使用显式构造函数来创建@987654331 @。它使用复制构造。

标签: c++ initialization language-lawyer c++17


【解决方案1】:

Z(1) 将直接初始化prvalue。然后,prvalue 将用于初始化对象。通过rules of guaranteed elision,没有临时跟随副本。 prvalue 直接初始化对象。因此,Z a3 = Z(1);完全等价于Z a3(1);

在 C++17 之前的版本中,这将执行 prvalue 临时的直接初始化,然后将临时的(几乎可以肯定是省略的)副本复制到对象 a3 中。无论副本是否被省略,prvalue的初始化都是通过直接初始化。 a3的初始化是通过拷贝初始化,但是这是通过拷贝构造函数,而不是explicit

【讨论】:

  • @WakeupBrazil 为什么您希望它不能编译?顺便说一句,它也应该用 c++98 编译。
  • @WakeupBrazil 那又怎样?它仍然是有效的代码。不一定要直接初始化才有效。
  • @WakeupBrazil 叹息。所以呢?它。做。不是。事情。直接初始化是指Z(1),正如答案中已经解释的那样。
  • @WakeupBrazil:我不确定你在说什么。 Z(1) 是直接初始化语法,正如您发布的链接所明确指出的那样。 Z a3 = ... 执行复制初始化,可能会或可能不会被忽略,但任何一种方式都不会调用任何显式构造函数。
  • @WakeupBrazil:“我说的是用于初始化 a3 的语法。在说什么并不重要。因为comment说的是用来初始化临时的语法。
【解决方案2】:

它在谈论Z(1)。 [dcl.init]/16:

在 [...] 功能符号类型中发生的初始化 转换 (5.2.3) [...] 被称为 直接初始化

然后使用纯右值来复制初始化z,这很好,保证省略或不 - Z 的复制/移动构造函数不是explicit,所以即使没有初始化也很好在 C++17 中保证省略。

【讨论】:

  • 现在对我来说很有意义。他们说这条线没问题,因为Z(1) 是直接初始化的(它必须是因为构造函数是显式的)。
  • Z a3 = Z(1); 怎么会使用直接初始化语法?
  • @WakeupBrazil Z(1) 的一部分。我什至给了你确切的报价,说它确实......
  • {[dcl.init]/16}(eel.is/c++draft/dcl.init#16) 解释了什么是直接初始化语法,这不是我在提到的示例中看到的。
  • @WakeupBrazil 所以它同时使用两者。所以呢?如果我说“您的用户名中使用了字母 B”,您是否抱怨它还使用了一堆其他字母?
【解决方案3】:

(请注意:我对新概念还不是很熟悉,但下面的分析似乎是正确的)。首先,您需要从函数转换表达式Z(1); 开始。请注意,这个 prvalue 表达式被称为使用 direct-initialization 初始化一些 result 对象这个对象是哪个,未指定并稍后决定,不是由这个函数转换表达式!

现在,关于纯右值的结果对象

prvalue的结果对象是prvalue初始化的对象; ... [... 对于丢弃的纯右值,临时对象被物化;见条款 [expr]。 ... ]

让我们首先为案例Z(1); 做一个例子,这应该有助于理解这个概念。这是一个表达式语句,是一个丢弃值表达式。对于这样的表达,规则说

...如果表达式是纯右值...,则应用临时实现转换([conv.rval])。

并且每当应用这种“临时实现”转换时,都会创建一个临时对象并从该prvalue初始化

此转换通过以临时对象作为其结果对象评估纯右值,从纯右值初始化一个类型为 T 的临时对象 ([class.temporary]),并生成一个表示临时对象的 xvalue

哇,像Z(1); 这样的简单语句需要这一切。现在谈谈你的情况,Z a3 = Z(1);。为此,result 对象 更直接地由 8.6p17

指定

如果初始化表达式是纯右值并且源类型的 cv 非限定版本与目标类是同一类,则使用初始化表达式初始化目标对象。

Viola,我们有一个表达式,其语义是“将未知对象 X 直接初始化 1”,然后另一个规则提供该对象“X”。特别是,每个类-prvalue 表达式直接创建对象的 C++17 之前的模型已经过时。这尤其意味着不涉及复制构造函数或移动构造函数,但据我所知,您的代码完全等效Z a3(1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 2010-09-12
    • 2015-09-19
    • 2016-09-14
    相关资源
    最近更新 更多