为什么部分初始化 class 然后调用委托 ctor 失败?

Why partially initializing a class and then invoking delegating ctor fails?

以下代码不会将结构字符串成员初始化为相同的值。

#include <string>
#include <iostream>

struct S
{
    std::string s1;
    std::string s2;
    S(std::string const& s) : s1{s}{}
    S(int i) : S([&]{
        s2 = std::to_string(i);
        return std::to_string(i);
    }())
    {}
};

int main()
{
    S s{123};
    std::cout << s.s1 << "|" << s.s2;

    return 0;
}

我在 gcc 中遇到分段错误(尝试了不同的版本), 和 123| 通过 Wandbox 在 clang 中(也有不同的版本)。

我遇到读取访问冲突 Visual Studio 15.9.16

谢谢。

你给构造函数(即 lambda)的参数不能访问内部成员,因为它们还没有被构造。

这应该有效:

struct S
{
    std::string s1;
    std::string s2;
    S(std::string const& s) : s1{s}{}
    S(int i)
      : S(std::to_string(i))
    {
        s2 = s1;
    }
};

检查未初始化的对象是一个 UB,在这种情况下它被这个字符串遮盖了:

       s2 = std::to_string(i);

这是对 std::stringoperator= 的调用,用于存储在 s2 位置的对象。 那时它还没有初始化。我假设您可以通过初始化 s2 来修复它,但是在初始化列表中调用构造函数之前不可能这样做。

您不能更改创建顺序,即使您以错误的顺序编写该部分。 s2 总是在 s1 之后初始化,两者都在构造函数调用之后但在进入其主体之前完成。因此顺序看起来像:

  1. 调用 ctor S(int) 并初始化参数 i
  2. 创建 lambda 对象,捕获 thisi
  3. 调用 lambda 的 operator()
  4. 为存储在 s2
  5. 位置的对象调用 std::stringoperator=
  6. 创建 std::string
  7. 引用创建的字符串,使用参数 s 委托 ctor S(std::string) 的调用。
  8. s1 的初始化值为 s
  9. s2
  10. 的默认初始化

项目符号 4. 和 8. 按顺序发生,这是不合法的,更糟糕​​的是,由于实现,它可能不会产生错误,因此 gcc 版本可能将该数据写入内存的某个随机区域。 MSVC,至少在调试版本中,可能会生成一些诊断代码,因为标准没有为这种情况定义程序行为。