Home > OS >  Declaring a variable and assigning with move assignment operator in one statement on Clang causes se
Declaring a variable and assigning with move assignment operator in one statement on Clang causes se

Time:10-23

I've got this trivial example of what I thought was calling the move assignment operator of this Test struct. Running it, it calls the move constructor and then seg faults on destruction on Clang. On MSVC, it works fine.

I'm a bit confused by that behavior cause i would expect it to construct with the parameterless constructor and then call the move assignment operator.

#include <iostream>

struct Test
{
Test() : data(nullptr), dataCount(0) {}

Test(Test&& other)
{
  std::cout << "mv cstr" << std::endl << std::flush;
  delete[] data;
  data = other.data;
  other.data = nullptr;

  dataCount = other.dataCount;
}

Test& operator=(Test&& other)
{
  std::cout << "mv op" << std::endl << std::flush;
  
  delete[] data;
  data = other.data;
  other.data = nullptr;

  dataCount = other.dataCount;
  return *this;
}

~Test()
{
  std::cout << "dstr " << (void*)this << std::endl << std::flush; 
  delete[] data;
  data = nullptr;
}

char* data;
size_t dataCount;
};

int main() {
    Test test;
    test.data = new char[3];
    test.dataCount = 3;

    Test newTest = std::move(test);
    return 0;
}

If I instead declare and then assign, it of course works as expected

int main() {
    Test test;
    test.data = new char[3];
    test.dataCount = 3;

    Test newTest;
    newTest = std::move(test);
    return 0;
}

I've read through the std::move, move assignment operator, and move constructor documentation a few times but I'm just not getting what's specifically different here or left up to the compilers that would give different behavior between MSVC and Clang.

What am I missing?

CodePudding user response:

The move constructor is going to be called, because that is exactly what the code you wrote is designed to do:

Test newTest = std::move(test);

There is no move assignment happening here, since a new object, newTest, is being constructed from the value on the right side of the =.

Since your move constructor has a call to delete[] data; and data is not initialized, undefined behavior occurs.

i would expect it to construct with the parameterless constructor and then call the move assignment operator.

If you think about this, this should not be expected. The compiler isn't going to take a circuitous route like this to construct the object, causing an unnecessary slow down.

What if the default constructor was "heavy", i.e. went through expensive operations to construct the object? The whole point of the move constructor is to not have to go through this step, and to simply "steal" the data from the object being moved from.

  • Related