2

This is basically a tagged union:

#include <string>
using std::string;

struct test{
    test():tag(INT),i(0){};
    test(const test&)=delete;
    test& operator=(const test&)=delete;
    enum {STRING,INT} tag;
    union {
        string s;
        int i;
    };
    test& operator=(string s){
        this->s=s;
        tag=STRING;
        return *this;
    }
    test& operator=(int i){
        this->i=i;
        tag=INT;
        return *this;
    }
    ~test(){
        if (tag==STRING)
            s.~string();
    }
};

int main(){
    test instance;
    instance="string";
    return 0;
}

It compiles but every time it crashes with a Segmentation fault. I'm just out of curiosity it's a pretty complete union class, provided a custom destructor, no move and no copy, then why will this crash? Must I use a string* in a union? If so, why?

timrau
  • 22,578
  • 4
  • 51
  • 64
YiFei
  • 1,752
  • 1
  • 18
  • 33

1 Answers1

4

Read carefully about the rule of five in C++11

This is wrong code:

 /// wrong code
  test& operator=(string s){
      this->s=s;
      tag=STRING;
      return *this;
  }

because you are wrongly presuming that this->s is valid before assigning it. You should use a placement new which constructs it (in an uninitialized memory zone starting at &this->s):

  test& operator=(string ss){
      if (tag==STRING) { s=ss; return *this; }
      new((void*)&s) std::string(ss);
      tag=STRING;
      return *this;
  }

BTW, I guess that you also should explicitly define

test(const test&s);
test(const std::string&);
test& operator=(const test&);
test(test&&s);
test& operator=(test&&);

BTW you are obviously missing the constructor:

test(const std::string&str) {
  tag = STRING;
  new ((void*)(&s)) std::string(str);
}
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547