====== 2016-05-23 - delete is back in town ======
back in [[wp>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 [[wp>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
#include
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:
{{:blog:2016:05:23:asan_error_from_invalid_pointer.png|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
#include
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:
{{:blog:2016:05:23:compile_error_on_delete.png|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
#include
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**! :)