简体   繁体   中英

C++ Weird behavior of operator= even when overloading it in another way

I want to learn more about C++ and just came along a - in my eyes - very weird behavior.

Question Summary

Since the detailed explanation is a bit long, here is a summary:

I've a Container with two member variables, of which only one can be filled with a public constructor. Both can be filled only in private. I've overloaded the copy and move for operator= in a way that the second data member can't be filled/accessed with the. BUT when returning a container with both member variables filled and writing something like

Container containerB = Container::returnContainerWithBothMembersFilled();

containerB has both member variables filled. This seems weird to me and that's not what I need/want.

Can someone explain to me how to solve that and why this is happening?

Detailed Explanation

In order to explain the behavior in more detail, here is a more detailed setup that reproduces my behavior:

Let's assume a class Container with two private members dataA_ and dataB_ of type std::vector<int> . There exists a public constructor to initialize dataA_ (but not! dataB_ ) and a private constructor to initialize both private members.

class Container {
public:
  Container() = default;
  Container(const Container& vector) = delete;

  explicit Container(std::vector<int> data) : dataA_(std::move(data)) {}

  // ... 

private:
  std::vector<int> dataA_;
  std::vector<int> dataB_;

  explicit Container(std::vector<int> dataA, std::vector<int> dataB)
      : dataA_(std::move(dataA)), dataB_(std::move(dataB)) {}
};

Further, there is a function static Container func() that returns an instance of Container with both data members set to some value.

  static Container func() {
    return Container(std::vector<int>(3,1), std::vector<int>(3,2));
  }

I have overloaded operator= in 2 different variants:

  • Container& operator=(const Container& other)
  • Container& operator=(Container&& other)

where all 2 variants have the same definition shown here for the first overload exemplary:

  Container& operator=(const Container& other) {
    if (this != &other) {
      dataA_ = other.dataA_;
    }
    return *this;
  }

FYI: all code above is written within class Container {... }; !

Question

Why does the following give me a container2 with a 'filled' and not empty dataB_ ?

int main() {
  std::cout << "init:" << std::endl;
  Container container1(std::vector<int>({1, 2, 3}));
  Container container2 = container1.func(); // doesnt work as expected
  Container container3;
  container3 = container1; // works, overloaded operator= is called and behavior is as expected

  return 0;
}
Container container2 = container1.func();

This is copy-initialization, not assignment, so operator= is irrelevant.

Before C++17 the initialization would be ill-formed, because it requires a constructor to be available which accepts a Container rvalue as argument (the result of the call container1.func() ). But you deleted the copy constructor and as a consequence there is also no move constructor either.

Since C++17 the initialization uses mandatory copy elision, meaning that the return value of container1.func() is directly constructed into container2 (because container1.func() is a prvalue of the same class type as container2 ). There is no constructor call involved either. You just get the return value from container1.func() in container2 and that one is constructed with both members filled.

Before C++17, even if the copy constructor was not deleted, but defined equiavalently to your operator= , it would still not work the way you want it to because the compiler was still allowed , but not required to elide the move/copy constructor call as is now mandatory since C++17. It would be up to the compiler whether or not container2 would have the second member empty. That's one of the reasons why it is a bad idea to change the semantics of the copy/move operations. This is one of a small number of situations where the compiler is allowed to perform optimizations which change the observable behavior of the program.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM