const correctness and parameters to functions with structs containing void*
Solution 1
The error you are observing has absolutely nothing to do with const qualifier used in function declaration, or with any const qualifiers you explicitly used in your code.
The problem is the same as in the following minimal example
void *p = "Hello";
which suffers from the same error.
In C++ language the type of string literal is const char [N]
. By the rules of const correctness it is convertible to const void *
, but it is not convertible to void *
. That's all.
A more formally correct error message would be "Cannot convert from const char [22]
to void *
", or "Cannot convert from const char *
to void *
", but apparently the inner workings of the compiler perform the conversion to const void *
first (under the hood) and then stumble on conversion to void *
, which is why the error message is worded that way.
Note that const correctness rules of C++ language used to include an exception that allowed one to implicitly convert string literals to char *
pointers. This is why
char *p = "Hello";
compiles with a mere warning, even though it violates the rules of const correctness just like the previous example. That exception applied only to conversions to char *
type, not to void *
type, which is why the previous example produces an error. This special conversion has been deprecated in C++03 and removed from the language in C++11. This is why the compiler issues a warning about it. (If you switch your compiler to C++11 mode it will become an error.)
Solution 2
First of all, your test class and function just make things unnecessarily complex. In particular, the error has nothing to do with the const
in Test * const pStruct
, because this only means that pStruct
must not be made to point to anything else. After all, in your own words:
the const modifier on the function definition makes it so that the pointer is constant but what it points to is not
Here is a simplified piece of code to reproduce the problem:
int main() {
void *ptr = "This is a test string"; // error
}
As for your question,
What I don't understand, given my understanding of const correctness, is why is the compiler emitting this error, "invalid conversion from ‘const void*’ to ‘void*’" at all? Since the const modifier on the function definition makes it so that the pointer is constant but what it points to is not, why should this be a problem?
Because a string literal is a char const[]
, "decaying" to a char const *
, and the conversion to a non-constant pointer would lose the const
qualifier.
This does not work for the same reason the following won't:
int main() {
int const *i; // what's pointed to by i shall never be modified
void *ptr = i; // ptr shall modify what's pointed to by i? error!
}
Or more precisely,
int main() {
int const i[22] = {}; // i shall never be modified
void *ptr = i; // ptr shall modify i? error!
}
If this was not an error, then you could use ptr
to implicitly bypass the const
qualifier of i
. C++ simply does not allow this.
Finally, let's look at this piece of code:
pStruct->pSomething = (void*)"This is a test string"; // no error now
Again, this can be simplified and reproduced with int
rather than char
so as not to obfuscate the real issue:
int main() {
int const i[22] = {}; // i shall never be modified
void *ptr = (void*)i; // ptr shall modify i? ok, I'll just shut up
}
You should not use C-style casts in C++. Use one of static_cast
, reinterpret_cast
, dynamic_cast
and const_cast
to make it clear which kind of conversion should be enforced.
In this case, you'd have seen the trouble it takes to "shut up" the compiler:
int main() {
int const i[22] = {};
void *ptr = const_cast<void*>(reinterpret_cast<void const *>(i));
}
And, of course, even though this may compile without a warning, the behaviour of the program is undefined because you must not use const_cast
to cast away the constness of an object originally initialised as constant.
Edit: I forgot about the whole char *
C compatibility business. But this is covered in the other answers already and, to my best understanding, does not render anything in my answer incorrect.
Solution 3
First of all, using C style casts will break const correctness. That is the only reason that your cast "works". So don't do it. Use reinterpret_cast
, which (should, I didn't test it) get you a similar error to the one you are seeing.
So "This is a test string"
is a const char*
. If you reference it as a void*
, something could modify the contents of void* later. If you make it so you can mess with the contents of that, you're no longer const correct.
And you COULD if there was no error, shown below.
int main() {
Test t;
TestFunc(&t);
reinterpret_cast<char*>(t.pSomething)[0]='?';
return 0;
}
Andrew Falanga
My first programming job was as a Test Engineer. That was the title and it was, essentially, a software engineer who's job was to write tests. This was in the Boise based Enterprise LaserJet R&D Lab. The languages used were TCL, KSH, BASH, C, C++ and, in the end, C#. Today I work for Micron Technology, Inc. as a Systems Software Engineer. My professional software engineering career has been focused mainly in libraries and lower level programs. I have written Linux kernel modules. I prefer to work in the Kernel before any other. User facing programs I tend to avoid.
Updated on June 05, 2022Comments
-
Andrew Falanga almost 2 years
I'm not going to say I fully understand the idea of const correctness but let's at least say that I understand it. So, when I encountered this, I was/am stumped. Can someone please explain this to me. Consider the following code:
#include <iostream> struct Test { int someInt; void * pSomething; }; void TestFunc(Test * const pStruct) { pStruct->someInt = 44; pStruct->pSomething = "This is a test string"; // compiler error here } int main() { Test t; TestFunc(&t); return 0; }
At the point I've annotated with a comment I get this error from gcc (4.5.3 for cygwin):
foo.cpp:10:24: error: invalid conversion from `const void*' to `void*'
It's apparently something to do with the fact that the struct contains a void* because some experimentation revealed that changing the struct to:
struct Test { int someInt; char * pSomething; };
produces a warning as opposed to an error. Also, leaving the structure unchanged but modifying this line to include the following cast compiles the code without warning:
pStruct->pSomething = (void*)"This is a test string"; // no error now
What I don't understand, given my understanding of const correctness, is why is the compiler emitting this error, "invalid conversion from ‘const void*’ to ‘void*’" at all? Since the const modifier on the function definition makes it so that the pointer is constant but what it points to is not, why should this be a problem? I'm assuming that there is some sort of implicit cast happening, as originally written, since the string literal would be something like
const char *
that must be converted tovoid *
. That notwithstanding, the question remains since whatpStruct
points to is not constant.For reference, I read up on const correctness here and here.
-
quantdev almost 10 years
char*
is special, there's an exception for it when it comes to pointer conversions -
Neil Kirk almost 10 yearsTo elaborate on @quantdev,
char *
is allowed to point to a string literal for backwards compatibility with C. But this is bad style. In C++ you should consider onlyconst char *
can point to a string literal.
-
-
Neil Kirk almost 10 yearsThere's no
std::
in front ofreinterpret_cast
-
Christian Hackl almost 10 yearsVC actually says "cannot convert 'const char [22]' to 'void *' - Conversion loses qualifiers".
-
Andrew Falanga almost 10 yearsI didn't intend to complicate things. Rather, I was using the "smallest amount of code that reproduces." You see, I'm bringing some C code into C++ and the function in question takes the sg_io_hdr_t struct. That struct has a
void *
to which the string literal is being assigned. Thank you for the detailed answer. This is quite helpful. -
Andrew Falanga almost 10 years@ChristianHackl I think that would have been more clear. I jumped to a conclusion. I'm quite glad that I asked here.
-
Christian Hackl almost 10 years@AndrewFalanga: The introducing line was not meant to criticise your question, of course. Sorry if it came across that way.