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

82 Views Asked by At

Following code does not initialize struct string members to same value.

#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;
}

I'm getting segmentation fault in gcc (tried different versions), and 123| in clang (different versions also) through Wandbox.

I'm getting a read access violation Visual Studio 15.9.16

Thank you.

2

There are 2 best solutions below

4
villintehaspam On BEST ANSWER

The argument you give to the constructor (i.e. the lambda) cannot access internal members, because they have not been constructed yet.

This should work:

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;
    }
};
0
Swift - Friday Pie On

Inspecting an uninitialized object is an UB, in this case it'd been obscured by this string:

       s2 = std::to_string(i);

It's a call to operator= of std::string for object stored at location of s2. It wasn't initialized at that point yet. I assume that you could fix it by initializing s2, but it's not possible to do before constructor call in initialization list.

You cannot change order of creation, even if you write that section in wrong order. s2 always initialized after s1, both done after constructor call but before entering its body. Thus the order looks like:

  1. Call of ctor S(int) with initialization of argument i;
  2. Creation of lambda object, capturing this and i
  3. Call of operator() of lambda
  4. Call operator= of std::string for object stored at location of s2
  5. Creation of std::string
  6. Delegating call of ctor S(std::string) with argument s with reference to created string.
  7. Initialization of s1 with value of s
  8. Default initialization of s2

Bullets 4. and 8. happen in order which isn't legal, even worse, due to implementation, it may not to yield an error, so gcc version likely writes that data into some random area of memory. MSVC, at least in debug version, may generate some diagnostic code, for standard doesn't define program behavior for such case.