包含void*结构的函数的Const正确性和形参
const correctness and parameters to functions with structs containing void*
我不会说我完全理解 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 (cygwin的4.5.3)得到了这个错误:
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正确性的理解,为什么编译器会发出这个错误,"从' const void* '到' void* ' 的无效转换"?既然函数定义上的const修饰符使得指针是常量,但它指向的不是,为什么这应该是一个问题?我假设有某种隐式强制转换发生,就像最初写的那样,因为字符串字面值将是像const char *
这样的东西,必须转换为void *
。尽管如此,问题仍然存在,因为pStruct
指向的是而不是常数。
作为参考,我在这里和这里阅读了const正确性。
您观察到的错误与函数声明中使用的const限定符或您在代码中显式使用的任何const限定符完全无关。
问题与下面的最小示例
相同void *p = "Hello";
也会出现同样的错误。
在c++语言中,字符串字面值的类型是const char [N]
。根据const正确性规则,它可以转换为const void *
,但不能转换为void *
。就是这样。
一个更正式正确的错误信息应该是"不能从const char [22]
转换到void *
",或者"不能从const char *
转换到void *
",但显然编译器的内部工作首先执行到const void *
的转换(在引线下),然后偶然转换到void *
,这就是为什么错误信息是这样写的。
请注意,c++语言的const正确性规则包含一个异常,该异常允许隐式地将字符串字面量转换为char *
指针。这就是为什么
char *p = "Hello";
即使像前面的例子一样,违反了const正确性规则,但编译时也只给出一个警告。该异常仅适用于到char *
类型的转换,而不适用于到void *
类型的转换,这就是前面的示例产生错误的原因。这种特殊的转换已在c++ 03中弃用,并在c++ 11中从语言中删除。这就是编译器发出警告的原因。(如果你将编译器切换到c++ 11模式,它将成为一个错误)
首先,您的测试类和函数只会使事情变得不必要地复杂。特别是,这个错误与Test * const pStruct
中的const
无关,因为这只意味着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
来隐式地绕过i
的const
限定符。c++根本不允许这样做。
最后,让我们看看这段代码:
pStruct->pSomething = (void*)"This is a test string"; // no error now
同样,这可以用int
而不是char
来简化和复制,以免混淆实际问题:
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
}
在c++中不应该使用C风格的强制转换。使用static_cast
, reinterpret_cast
, dynamic_cast
和const_cast
中的一个来明确应该执行哪种转换。
在这种情况下,您已经看到了"关闭"编译器所带来的麻烦:
int main() {
int const i[22] = {};
void *ptr = const_cast<void*>(reinterpret_cast<void const *>(i));
}
当然,即使这可以在没有警告的情况下编译,程序的行为也是undefined,因为您不能使用const_cast
来抛弃最初初始化为常量的对象的constness。
编辑:我忘记了整个 char *
c兼容性业务。但这在其他答案中已经涵盖了,据我所知,我的答案中没有任何错误。
首先,使用C风格强制转换会破坏const的正确性。这是你的演员"有效"的唯一原因。所以不要这么做。使用reinterpret_cast
,它(应该,我没有测试它)会得到与您看到的类似的错误。
所以"This is a test string"
是const char*
。如果将其作为void*
引用,稍后可能会修改void*的内容。如果你这样做,你就可以对其中的内容进行修改,你就不再是const correct了。
如果没有错误,则可以,如下所示。
int main() {
Test t;
TestFunc(&t);
reinterpret_cast<char*>(t.pSomething)[0]='?';
return 0;
}
"blah"
是一个包含5个char const
的数组。在c++ 11中,它隐式转换为char const*
。在c++ 03和更早的版本中,字面值也隐式地转换为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及更早版本中,由于源是字面值字符串,因此您得到char const[5]
→char*
.
- 使用mem_fun_ref if成员函数需要多个形参
- 通过类的模板形参特化成员模板结构
- 非类型引用形参/实参
- 哪个模板形参在boost::shared_ptr构造函数中使用一个原始指针
- 如何确保迭代器模板形参与模板类的模板形参具有相同的数据类型
- 如何在编译时通过模板形参默认值的名称/指针获取函数的类型
- c++:候选模板被忽略:模板形参显式指定的参数无效
- c++中作为形参的指针
- 哪种方法更适合为函数提供编译时间常数?函数实参与模板形参
- 包含void*结构的函数的Const正确性和形参
- 传递boost::函数,该函数接受一个模板实参作为默认为NULL的形参
- 将右值引用形参强制转换为右值引用
- 给引用形参赋值使对象无效
- 当实参是初始化列表而形参是引用时,重载解析
- const整型模板形参的条件
- 使用作为模板形参提供的基类成员,不带限定符
- 模板类的不同返回类型取决于类的形参
- 模板形参有二义性:无法推断模板实参
- 是否有必要在定义中使用模板形参来引用同一个类?
- c++ std::find()和模板形参