我不会说我完全理解const正确性的想法,但至少我要理解它。所以,当我遇到这个时,我感到很难过。有人可以向我解释一下。请考虑以下代码:
#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;
}
在我注释了注释时,我从gcc(4.5.3 for cygwin)中得到了这个错误:
foo.cpp:10:24: error: invalid conversion from `const void*' to `void*'
这显然与结构包含void *这一事实有关,因为一些实验显示将结构更改为:
struct Test {
int someInt;
char * pSomething;
};
产生警告而不是错误。此外,保持结构不变,但修改此行以包含以下转换编译代码而不发出警告:
pStruct->pSomething = (void*)"This is a test string"; // no error now
我不明白,鉴于我对 const correctness 的理解,为什么编译器会发出此错误,“无效转换为'const void *'到'void *' “根本不是?由于函数定义中的const修饰符使得指针是常量但它指向的不是,为什么这应该是一个问题呢?我假设存在某种隐式转换,如最初编写的那样,因为字符串文字类似于const char *
,必须转换为void *
。尽管如此,问题仍然存在,因为pStruct
指向不不变。
作为参考,我读了const correctness here和here。
答案 0 :(得分:4)
您正在观察的错误与函数声明中使用的const限定符或您在代码中明确使用的任何const限定符完全无关。
问题与以下最小例子
相同void *p = "Hello";
遭受同样的错误。
在C ++语言中,字符串文字的类型为const char [N]
。根据const正确性的规则,它可以转换为const void *
,但它不能转换为void *
。这就是全部。
更正式的错误消息是&#34;无法从const char [22]
转换为void *
&#34;或&#34;无法从const char *
转换为{{ 1}}&#34;,但显然编译器的内部工作首先执行转换到void *
(在引擎盖下)然后偶然转换到const void *
,这就是为什么错误消息是这样说的。
请注意,C ++语言的const正确性规则曾经包含一个允许将字符串文字隐式转换为void *
指针的异常。这就是为什么
char *
仅仅像前面的例子那样违反了const正确性的规则,仅仅会发出警告。该异常仅适用于转换为char *p = "Hello";
类型,而不适用于char *
类型,这就是前一个示例产生错误的原因。此特殊转换已在C ++ 03中弃用,并已从C ++ 11中的语言中删除。这就是编译器发出警告的原因。 (如果将编译器切换到C ++ 11模式,则会出错。)
答案 1 :(得分:1)
首先,使用C样式转换将破坏const正确性。这是你的演员&#34;工作的唯一原因&#34;。所以不要这样做。使用reinterpret_cast
,(应该,我没有对其进行测试)会让您遇到类似的错误。
所以"This is a test string"
是const char*
。如果您将其引用为void*
,则可能会稍后修改void *的内容。如果你这样做你可以弄乱它的内容,你就不再是正确的了。
如果没有错误,你可以这样做,如下所示。
int main() {
Test t;
TestFunc(&t);
reinterpret_cast<char*>(t.pSomething)[0]='?';
return 0;
}
答案 2 :(得分:1)
首先,您的测试类和函数只会使事情变得不必要地复杂化。特别是,该错误与const
中的Test * const pStruct
无关,因为这仅表示pStruct
不得指向其他任何内容。毕竟,用你自己的话说:
函数定义上的const修饰符使得它成为了 指针是常量但它指向的不是
以下是重现问题的简化代码:
int main() {
void *ptr = "This is a test string"; // error
}
关于你的问题,
鉴于我对const正确性的理解,我不明白, 是为什么编译器会发出此错误,“无效转换来自 'const void *'到'void *'“在所有?自从const修饰符上 函数定义使得指针是常量但是什么 它指的不是,为什么这应该是一个问题呢?
因为字符串文字是char const[]
,“衰减”到char const *
,并且转换为非常量指针会丢失const
限定符。
这不起作用,原因与以下相同:
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!
}
或者更准确地说,
int main() {
int const i[22] = {}; // i shall never be modified
void *ptr = i; // ptr shall modify i? error!
}
如果这不是错误,那么您可以使用ptr
隐式绕过const
的{{1}}限定符。 C ++根本不允许这样做。
最后,让我们看一下这段代码:
i
同样,这可以使用pStruct->pSomething = (void*)"This is a test string"; // no error now
而不是int
进行简化和复制,以免混淆真正的问题:
char
你不应该在C ++中使用C风格的强制转换。使用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
}
,static_cast
,reinterpret_cast
和dynamic_cast
之一来明确应执行哪种转化。
在这种情况下,您已经看到了“关闭”编译器所需的麻烦:
const_cast
当然,即使这可能在没有警告的情况下编译,程序的行为也是未定义,因为您不能使用int main() {
int const i[22] = {};
void *ptr = const_cast<void*>(reinterpret_cast<void const *>(i));
}
来抛弃对象的常量初始化为常数。
修改:我忘记了整个const_cast
C兼容性业务。但是这已经包含在其他答案中了,据我所知,我的答案中的任何内容都不正确。
答案 3 :(得分:0)
"blah"
是一个5 char const
的数组。在C ++ 11中,它隐式转换为char const*
。在C ++ 03及更早版本中, literal 也被隐式转换为char*
,以实现C兼容性,但该转换已弃用,并且已在C ++ 11中删除。
设置
void* p = "blah"; // !Fails.
您获得了转化序列char const[5]
→char const*
→void*
,其中最后一个无效并产生错误。
设置
char* p = "blah"; // Compiles with C++03 and earlier.
使用C ++ 11,您获得与void*
相同的转换,并且出现错误,但是使用C ++ 03及更早版本,因为源代码是 literal 字符串char const[5]
→char*
。