【问题标题】:Why does "ctor() = default" change behavior when other constructors are present?为什么存在其他构造函数时“ctor() = default”会改变行为?
【发布时间】:2015-02-22 22:11:46
【问题描述】:

为什么

struct wrapper
{
    explicit wrapper(void *);
    wrapper() = default;
    int v;
};

int main() { return wrapper().v; }  // You should run this in Debug mode

返回0xCCCCCCCC,而

struct wrapper { wrapper() = default; int v; };
int main() { return wrapper().v; }

struct wrapper { int v; };
int main() { return wrapper().v; }

都返回0?

【问题讨论】:

  • 当你添加 non-=default 构造函数时,类型不再是聚合。我敢肯定这是在发挥作用,但不知道如何。我怀疑在任何一种情况下都不能保证它为 0。
  • @RyanHaining:你确定吗?如果是这样,那似乎很奇怪,因为这意味着您唯一可以使用= default 的时候是没有其他构造函数存在时,这似乎毫无意义...
  • =default 将返回默认构造函数,但v 在这两种情况下都未初始化。使用 gcc 即使在第一种情况下我也会得到 0,但这一切都是偶然的。
  • @RyanHaining:如果您将构造函数显式声明为wrapper() { }(即避免初始化v),在GCC 的第一种情况下您仍然会得到零吗? (不要忘记在没有优化的调试模式下进行这两个实验。)
  • 在这种情况下,我不会,不,我也获得了巨大的价值。我认为我实际上对 0 保证有误。我在看atm的规则

标签: c++ visual-c++ constructor default-constructor visual-c++-2013


【解决方案1】:

值初始化期间,如果T 是一个没有用户提供或删除的默认构造函数的类类型,则对象是零初始化(§ 8.5/8.2)。 wrapper确实是这样。

您的第一个示例匹配零初始化的第三种情况(第 8.5/6.1 节,强调我的)

——如果T是标量类型(3.9),则对象初始化为整型字面量转换得到的值 0(零)到T

如果T 是(可能是 cv 限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都初始化为零,填充初始化为零位;

—如果T 是(可能是 cv 限定的)联合类型,则对象的第一个非静态命名数据成员初始化为零,填充初始化为零位;

——如果 T 是一个数组类型,每个元素都是零初始化的

——如果 T 是引用类型,则不执行初始化

所以在你的第一个例子中,v 应该是零初始化的。这看起来像一个错误。

在您的第二个和第三个示例中,您不再有用户提供的构造函数,但您确实有一个不是用户提供或删除的默认构造函数,因此您的示例仍然属于零初始化的第三种情况,这是对每个非静态数据成员进行零初始化。 VS 在那里是正确的。

【讨论】:

  • C++11 标准存在缺陷。如果您阅读 IS,它会说“没有用户提供的构造函数”,缺少关键的“默认值”。
  • @Mehrdad :D 哦,这最近变得模棱两可——我指的是国际标准。另见:open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1301
  • @dyp 编辑会影响答案吗?
  • @dyp:哈哈,如果我认为你的意思是这样的话,那很有趣。我明白了,哈哈,谢谢你的链接。
  • @0x499602D2 我不完全确定您指的是什么。语言律师可能会争辩说 VS 的行为完全符合要求,因为第一个包装器确实有一个用户提供的构造函数(转换 ctor)。 -- 编辑:嗯,user-provided 只为特殊的成员函数定义..
【解决方案2】:

这似乎是 MSVC 中的一个错误。在所有三种情况下,wrapper 都没有用户提供的默认构造函数,因此使用 wrapper() 进行初始化会调用:

(来自n3690的所有引用)

(8.5/11) 初始化器为空括号集的对象,即 (),应进行值初始化。

(感谢 dyp),这将导致 int v 的零初始化

然后初始化将我们引向规则:

(8.5/8) 如果 T 是(可能是 cv 限定的)类类型,没有用户提供或删除的默认构造函数,则该对象为零初始化,并检查默认初始化的语义约束。

零初始化规则状态:

(8.5/6) 如果 T 是(可能是 cv 限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都被初始化为零并且填充被初始化为零位

int vwrapper 的数据成员,其自身初始化为零,根据:

(8.5/6)如果T是标量类型(3.9),则将对象初始化为整型文字0(零)转换为T得到的值

这不是您观察到的行为。

【讨论】:

  • 如果您认为 VC++ 不这样做是正确的,那么如果您能解释为什么 GCC 和 Clang 会经历额外的麻烦来对所有内容进行值初始化,那就太好了。
  • @Mehrdad 我扭转了一切
  • @dyp 这是我曾经(被推到)命中的最微妙的 c++ 点。
猜你喜欢
  • 1970-01-01
  • 2019-04-24
  • 2014-08-14
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 1970-01-01
  • 2018-06-16
  • 2018-03-25
相关资源
最近更新 更多