Home > Net >  How to return a class instance on the heap, when the relevant ctor is private?
How to return a class instance on the heap, when the relevant ctor is private?

Time:10-08

Suppose I have this struct

struct MyStruct {

  static MyStruct Create(int x) {
    return { x*2, x>3 };
  }

  MyStruct(const MyStruct& c) = delete;  // no copy c'tor

private:
  MyStruct(int a_, bool b_) : a(a_), b(b_) {}  // private c'tor -- can't use new

  const int a;
  const bool b;
};

Edit: I deleted the copy constructor. This is simplified example of some classes I have in my codebase where they don't have copy c'tors.

I can get an instance on the stack like so:

int main() {
  auto foo = MyStruct::Create(2);
  return 0;
}

But suppose I need a pointer instead (or unique_ptr is fine), and I can't change the implementation of MyStruct, how can I do that?

CodePudding user response:

Is this what you're looking for?

auto baz  = std::make_unique<MyStruct>( MyStruct::Create(2) );  // unique pointer

CodePudding user response:

You could wrap MyStruct in another class, which has a MyStruct member. Here's a minimal version of that:

class Wrapper {
public:
    MyStruct ms;
    Wrapper(int x) : ms(MyStruct::Create(x)) { }
};

which you can use like so:

int main() {
  MyStruct::Create(2);
  std::make_unique<Wrapper>(2);
}

I think this shouldn't be making any copies nor moves.

You can then add any other constructors and methods you like, possibly forwarding some of the method calls to ms. Some might choose to make ms protected or private.


If you're wondering why no copies are made - that's due to copy elision:
What are copy elision and return value optimization?

CodePudding user response:

A comment rather than an answer, to avoid confusion for future readers.

I can get an instance on the stack like so:

int main() {
  auto foo = MyStruct::Create(2);
  return 0;
}

Note that this is only true as of C 17 and guaranteed copy elision, whereas the program is ill-formed is C 14, as even if the copy may be elided, the initialization of foo is copy-initialization from a temporary (in C 17: the temporary is never materialized).

CodePudding user response:

One more way to do it:

  struct ChildStruct : public MyStruct {                                                                                  
      ChildStruct(int x) : MyStruct(MyStruct::Create(x))                             
      {}                                                                             
                                                                                     
  };                                                                                 
                                                                                     
  int main() {                                                                       
    MyStruct *foo1 = new ChildStruct(2);                                             
    return 0;                                                                        
  }  

CodePudding user response:

C style solution. I am not sure that this is not UB, but for simple struct with 2 integer fields it should work.

int main() {                                                                    
    auto foo = MyStruct::Create(2);                                               
                                                                                  
    MyStruct *p = (MyStruct*)malloc(sizeof(MyStruct));                            
    memcpy(p, &foo, sizeof(MyStruct));                                            
    //...
    free(p);
    return 0;                                                                     
}
  • Related