In C++, is there any value in using a const void *
for an argument type to a function over a void *
? Since a void *
is opaque, is there any risk of modification other than if the user does reinterpret_cast
, in which case they could likewise do const_cast
on a const void *
and thus does one really buy anything? I ask because I was using a utility template class for shared pointers which provided a specialization on void
to avoid void &
issue but no specialization was provided for const void
and thus I wonder whether this was just an oversight or should it never be needed?

- 41,123
- 68
- 193
- 295
-
4There is no need for a `reinterpret_cast`, a `static_cast` suffices to convert to any other type. – David Rodríguez - dribeas Jul 09 '12 at 15:47
6 Answers
It offers the same benefit that const
offers on other pointer types: you can't modify what is pointed to unless you cast away the const
-ness explicitly. In interfaces, const void*
is a sign to client code that whatever you pass in may be read but not written to. E.g., std::memcpy
is declared as
void *memcpy(void *dest, const void *src, std::size_t count);
which signals that it will read src
and write to dest
. Of course, if it were really implemented in C++ (possible but not likely), it has to cast both pointers to other types.
If you feel that this "doesn't buy you anything", then it's the const
keyword per se that apparently has no value.

- 355,277
- 75
- 744
- 836
-
Exactly, `const` is very informative for the API's user. For example, just bringing up the prototype in an IDE as you write a function call then tells you which of the arguments are mutable like in `memcpy()`. – olovb Jul 09 '12 at 17:39
-
Furthermore it's annoying when the caller has a const pointer and has to cast away the const-ness (to avoid a warning) when the declared argument type really should've been `const`. – olovb Jul 09 '12 at 17:49
memcpy
takes two pointer parameters, one void*
and the other const void*
. The second parameter can be implicitly converted from a const char*
(or other pointer-to-const-object-type) argument, whereas the first one can't.
That absence of implicit conversion is the value -- it forces the user to deliberately cast away const in the (unlikely) event that they want to, rather than accidentally discarding it.
Then within an implementation of memcpy
, or a similar function, the programmer would have to const_cast
or C-style-cast the const void*
parameter before attempting to modify its referand. They would be able to static_cast
the non-const parameter and modify its referand. The kind of cast you need to write hopefully tells you something about whether what you're doing is sensible.
I think that if your shared_ptr helper functions need to treat void
specially, then they would need to treat all cv-qualified void
specially. So that's four cases: void
, const void
, volatile void
, const volatile void
. But if users of the functions have in the past tried it on a shared_ptr<void>
, and complained that it didn't work, but have never tried it on a shared_ptr<const void>
, then maybe the issue hasn't arisen.
Maybe shared_ptr<void>
is already unusual enough that it hasn't come up. Maybe the kind of person who uses a shared_ptr<void>
tends not to mind casting away cv-qualifiers, on the basis that whenever someone eventually restores the correct type, they will also restore the correct qualifiers.
Come to think of it -- does shared_ptr<const void>
work at all, or does the code in shared_ptr
that calls the deleter need an implicit conversion from T*
to void*
? I don't rememember whether I've ever used a shared_ptr<const T>
.

- 273,490
- 39
- 460
- 699
Don't forget the "documentation value" of const
. Even though someone can always cast it away, the const
serves to indicate the original intent that the thing being pointed to should not be changed via the pointer. const_cast
(and reinterpret_cast
for that matter) should always be used with care, and should give a programmer pause if/when they are necessary.

- 4,470
- 2
- 19
- 27
Well yes, there are (some of) the same advantages that const
always has: it documents the fact that the contents aren’t supposed to be mutated.
Imagine the following code:
int const object = some_value();
some_function(&object);
This call only compiles if the function argument was declared as void const*
, otherwise the client would need a const_cast
to cast away constness. Of course, we neither want the client to have this inconvenience, nor do we want them to lie about their data (by casting away constness).

- 530,221
- 131
- 937
- 1,214
There is still a benefit in "self-documenting" the code.
store_pod(const void* data, std::size_t bytes);
without any comment lets you see that the pointed-to data will not be modified.
Also, note that to break that const
promise, a function would need to do both a const_cast
and a reinterpret_cast
.

- 70,891
- 9
- 107
- 161
As with all uses of const
it serves two purposes. On the implementation of the function it will help the compiler detect misuses, which as you mention can be forced and silenced by means of a const_cast
(or a C-style cast).
But const
serves a second purpose, it offers the promise that the object will not be modified and in doing so enables users to pass pointers to const objects (on the assumption that you will keep your promise), effectively enabling a wider use of your function. This can be seen with this simple example:
void foo( const void* );
void bar( void* );
int main() {
const int value = 10;
foo( &value ); // correct, the function promises not to modify the value
//bar( &value ); // error, this would break const correctness
}

- 204,818
- 23
- 294
- 489
-
`bar` is also declared as taking in a `const void*`. Am I missing something as to why that would break const correctness by calling it with `&value`, or is that just a typo? – Dan F Jul 09 '12 at 17:16
-
@DanF: *sleep deprivation*, that is what **I** am not missing. Thanks for bringing the typo to attention. The `bar` function was intended to take the argument by non-const `void *` – David Rodríguez - dribeas Jul 09 '12 at 17:30