Ruggero Turra
Ruggero Turra

Reputation: 17680

Move constructor for std::string from char*

I have a function f returning a char*. The function documentation says:

The user must delete returned string

I want to construct a std::string from it. The trivial things to do is:

char* cstring = f();
std::string s(cstring);
delete cstring;

Is it possibile to do it better using C++ features? I would like to write something like

std::string(cstring)

avoiding the leak.

Upvotes: 10

Views: 11267

Answers (4)

Ben Voigt
Ben Voigt

Reputation: 283713

This code can never be correct:

std::string s(cstring);
delete cstring;

The std::string constructor that takes a character pointer, requires a NUL-terminated string. So it is multiple characters.

delete cstring is scalar delete.

Either you are trying to create a string from a character scalar (in which case, why the indirection?)

std::string s(cstring[0]);
delete cstring;

or you have multiple characters, and should delete accordingly

std::string s(cstring);
delete [] cstring;

Check the other answers for the recommended way to make sure delete[] gets used, e.g.

std::string(std::unique_ptr<char[]>(f()).get())

Upvotes: 1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275600

std::string steal_char_buffer( std::unique_ptr<char[]> buff ) {
  std::string s = buff?buff.get():""; // handle null pointers
  return s;
}
std::string steal_char_buffer( const char* str ) {
  std::unique_ptr<char[]> buff(str); // manage lifetime
  return steal_char_buffer(std::move(buff));
}

now you can type

std::string s = steal_char_buffer(f());

and you get a std::string out of f().

You may want to make the argument of steal_char_buffer be a const char*&&. It is mostly pointless, but it might lead to some useful errors.

If you can change the interface of f, make it return a std::string directly or a std::unique_ptr<char[]>.

Another good idea is to wrap f in another function that returns a std::unique_ptr<char[]> or std::string:

std::unique_ptr<char[]> good_f() {
  return std::unique_ptr<char[]>(f());
}

and/or

std::string good_f2() {
  auto s = good_f();
  return steal_char_buffer( std::move(s) );
}

Upvotes: -1

Praetorian
Praetorian

Reputation: 109169

std::string will make a copy of the null terminated string argument and manage that copy. There's no way to have it take ownership of a string you pass to it. So what you're doing is correct, the only improvement I'd suggest is a check for nullptr, assuming that is a valid return value for f(). This is necessary because the std::string constructor taking a char const * requires that the argument point to a valid array, and not be nullptr.

char* cstring = f();
std::string s(cstring ? cstring : "");
delete[] cstring;   // You most likely want delete[] and not delete

Now, if you don't need all of std::string's interface, or if avoiding the copy is important, then you can use a unique_ptr to manage the string instead.

std::unique_ptr<char[]> s{f()}; // will call delete[] automatically

You can get access to the managed char * via s.get() and the string will be deleted when s goes out of scope.

Even if you go with the first option, I'd suggest storing the return value of f() in a unique_ptr before passing it to the std::string constructor. That way if the construction throws, the returned string will still be deleted.

Upvotes: 12

Deduplicator
Deduplicator

Reputation: 45664

There is no standard way for a std::string to take ownership of a buffer you pass.

Nor to take responsibility of cleaning up such a buffer.

In theory, an implementation, knowing all the internal details, could add a way for a std::string to take over buffers allocated with their allocator, but I don't know of any implementation which does.
Nor is there any guarantee doing so would actually be advantageous, depending on implementation-details.

Upvotes: 3

Related Questions