2016-05-23 - delete is back in town

back in C++98 times, during code review, my rule of thumb was: “if there is a delete in your code, you probably did something wrong”. they idea is that smart pointers ought to be used for managing lifetime of dynamically allocated memory. using raw pointers with ownership is literally asking for troubles.

time passed on and now we have C++14. the smart pointers note still holds. in fact it holds even more, thanks to move-semantics! what is changed is having delete in code base is not a bad thing any more. it is bad, if it used for memory. one common example is class that is non-copyable and non-movable:

struct NoCopyNoMove
{
  // disallow copying:
  NoCopyNoMove(NoCopyNoMove const&) = delete;
  NoCopyNoMove& operator=(NoCopyNoMove const&) = delete;
  // disallow moving:
  NoCopyNoMove(NoCopyNoMove&&) = delete;
  NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
};

it's a canonical example. common code nowadays. not everyone do however delete can be used in a very creative way, to disallow invalid code. do you see a problem in the code below?

#include <string>
#include <iostream>
 
char const* f(std::string const& s) { return s.data()+4; }
 
int main()
{
  auto ptr = f("test string for debugging");
  std::cout << ptr << "\n";
}

neither gcc nor clang did not notice any issue. address sanitizer did helper with a colorful kaboom note:

asan error report

the problem is line:

auto ptr = f("test string for debugging");

that actually returns a pointer to a temporary std::string, passed as an argument to f(). note that function f() is itself valid – it operates on data it got as an argument. it's a caller's code that is wrong! compiler does not help… or could it?

it actually turns out you can turn this tricky, undefined behavior, run time problem… into a trivial compile time error! what you want is f() does not work, when it is given a temporary! this is just a single line in C++:

#include <string>
#include <iostream>
 
char const* f(std::string const& s) { return s.data()+4; }
char const* f(std::string&& s) = delete; // <-- ... and fixed!
 
int main()
{
  auto ptr = f("test string for debugging");
  std::cout << ptr << "\n";
}

now when you try to compile, you get nice, readable, self-describing error message:

compile error from clang++

aha! so we're trying to use a function, returning pointer from an argument object, with a temporary that gets invalidated! let me fix that:

#include <string>
#include <iostream>
 
char const* f(std::string const& s) { return s.data()+4; }
char const* f(std::string&& s) = delete;
 
int main()
{
  auto str = std::string{"test dsfjsdlf sdf sd fsdf"};
  auto ptr = f(str); // <-- non-temporary object passed for processing
  std::cout << ptr << "\n";
}

+1 for code that cannot be used incorrectly. :) delete is back in town – and it's BIG! :)

blog/2016/05/23/delete_is_back_in_town.txt · Last modified: 2016/05/23 18:54 by basz
Back to top
Valid CSS Driven by DokuWiki Recent changes RSS feed Valid XHTML 1.0