7

I use an extern C function that returns dynamically allocated const char*.

I want to use unique_ptr<const char, decltype(std::free)> to manage it.

But there is no std::free(const void*) overload so I get invalid conversion from 'const void*' to 'void*' and have to use const_cast<char*>().

Is this just a Standard Library imperfection or there is something else behind it?

sad1raf
  • 135
  • 9
  • 3
    @ForceBru, the pointer is not `const`, it points to `const` memory. – sad1raf Mar 31 '17 at 14:30
  • 2
    @ForceBru: Yet it's ok to [`delete` `const` pointers](http://stackoverflow.com/q/755196/10077). – Fred Larson Mar 31 '17 at 14:30
  • @FredLarson, didn't know that, thanks for the info! – ForceBru Mar 31 '17 at 14:32
  • 3
    If the function is giving back something you're supposed to `free`, it shouldn't be a pointer-to-const. `free` is well within its rights to modify the memory it frees AFAIK. This is a problem with the function, not `free`. I don't know of any case where dynamically allocated memory would be unmodifiable (and this is the part where someone points out a little-known system). – chris Mar 31 '17 at 14:33
  • 3
    Note that `const T*` is not always a pointer to a `const T`. It can also mean a "`const` view" on a mutable `T`. When you allocate memory with `malloc`, the returned memory is always considered mutable (or else, you couldn't do anything with it). This means that it is always safe to `const_cast` away your `const T*` if you intend to `free` it (since it implies it was allocated with `malloc`, or else it's UB). – KABoissonneault Mar 31 '17 at 14:40
  • 1
    @chris I already used that pattern in cases where I store a pointer to a c style string and nobody shall be able to modify that string. Only one central function was sometimes free-ing the memory and strdup-ing a new string value. For external clients of the class I could separate implementation and getter. But I also wanted to have const as some simple protection for inside the class. If you have a better pattern for that, I'ld like to hear it. – Werner Henze Mar 31 '17 at 14:44
  • @chris, nevertheless, I am allowed to do this `delete[] new const char[2]{'t', 0};` – sad1raf Mar 31 '17 at 14:54
  • @WernerHenze, In general, if I have a resource, I like its lifetime management to be contained within a single class, and the class ideally doesn't do much else. For memory, that means it can store it non-const and it or whatever uses it can expose the memory as const all it likes, the class responsible for deallocating it knows it's not const without a cast or anything. For a string, it's entirely possible to have a class that exposes a `std::string_view` for its internal memory, and that's all it does, where the real class uses only the view for protection. – chris Mar 31 '17 at 14:56
  • @sad1raf, I can at least see where freeing a pointer-to-const would be useful if the external function initializes the data and allows no further modification beyond that. However, it's still a poor decision because it requires a cast in order to free it. If doing this, the library should have a specific function available to safely free these strings without ugly casts. As for `delete`, I could entirely see the `const` here being a view to initialized data that shouldn't change, yet backed by mutable memory. In that case, destruction through `const` works fine and deallocation could zero etc. – chris Mar 31 '17 at 15:06

1 Answers1

3

std::free is inherited from the C standard library. C does not have overloading, so a const overload could not have been inherited.

While the C++ standard library has extended the inherited C library with some useful overloads, a const overload has not been added for free.

Either such an overload has never been considered, or it has not been considered necessary enough to warrant its addition to the standard. I have not come across any openly available proposal for such an addition -- although I haven't read all proposals there have ever been, nor have I attended any committee meetings or workshops, so I cannot deny the possibility of the existence of such a document.

To work around the lack of such an overload, you can indeed use const_cast. In this context, it is completely safe.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    This would not require an overload. A single function taking `const void *` would work in both C and C++. –  Mar 31 '17 at 15:39
  • @hvd Sure. But has the C++ standard changed any of the function signatures of C library functions? Would the committee even consider such option? I wouldn't assume so, but perhaps. Perhaps the C library will be changed, but I find it unlikely to be a priority for them considering implicit const T* -> T* conversion is legal in C. – eerorika Mar 31 '17 at 16:13
  • Yes, the C++ standard has changed function signatures of C library functions (see `strchr`), but no, that isn't what I meant. *If* the C committee were to declare that `free(const void *)` makes sense and adjusted its signature accordingly, C++ could adopt that. And no, standard C does not have an implicit `const T *` -> `T *` conversion, and never has. C compilers are free to support it as an extension. C++ compilers are also allowed to support it as an extension, it's just that it's more difficult to do it correctly in C++ as it might break SFINAE uses, that's why C++ compilers usually don't –  Mar 31 '17 at 16:42
  • @hvd thanks for correcting me on the implicit conversion. I'm glad to be wrong about that. – eerorika Mar 31 '17 at 17:59