简体   繁体   English

具有包含void *的结构的函数的const正确性和参数

[英]const correctness and parameters to functions with structs containing void*

I'm not going to say I fully understand the idea of const correctness but let's at least say that I understand it. 我不会说我完全理解const正确性的概念,但至少可以说我理解它。 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): 在这一点上,我已经注释了,这是我从gcc(cygwin为4.5.3)得到的错误:

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: 这显然与结构包含void *有关,因为一些实验表明将结构更改为:

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? 考虑到我对const正确性的理解,我不明白的是为什么编译器会发出此错误,即“ 从'const void *'到'void *'的无效转换 ”? 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? 由于函数定义上的const修饰符使它成为常数,而指针指向的不是,所以为什么这会成为问题? 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 to void * . 我假设发生了某种隐式强制转换,如最初编写的那样,因为字符串文字将是类似const char * ,必须转换为void * That notwithstanding, the question remains since what pStruct points to is not constant. 尽管如此,仍然存在问题,因为pStruct指向的内容不是恒定的。

For reference, I read up on const correctness here and here . 作为参考,我在这里这里阅读了const正确性

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. 您观察到的错误与函数声明中使用的const限定符或您在代码中显式使用的任何const限定符绝对无关。

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] . 在C ++语言中,字符串文字的类型为const char [N] By the rules of const correctness it is convertible to const void * , but it is not convertible to void * . 根据const正确性规则,它可以转换为const void * ,但不能转换为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. 形式上更正确的错误消息是“无法从const char [22]转换为void * ”或“无法从const char *转换为void * ”,但是显然编译器的内部工作执行了到const void *的转换const void *首先(在幕后),然后偶然转换为void * ,这就是为什么错误消息是这样写的。

Note that const correctness rules of C++ language used to include an exception that allowed one to implicitly convert string literals to char * pointers. 请注意,C ++语言的const正确性规则曾经包含一个例外,该例外允许人们将字符串文字隐式转换为char *指针。 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. 就像前面的示例一样,即使它违反了const正确性规则,编译时也只是发出警告。 That exception applied only to conversions to char * type, not to void * type, which is why the previous example produces an error. 该异常仅适用于转换为char *类型,而不适用于void *类型,这就是前面的示例产生错误的原因。 This special conversion has been deprecated in C++03 and removed from the language in C++11. 此特殊转换已在C ++ 03中弃用,并已从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.) (如果将编译器切换到C ++ 11模式,它将成为错误。)

First of all, using C style casts will break const correctness. 首先,使用C样式强制转换会破坏const的正确性。 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. 使用reinterpret_cast ,它(应该,我没有对其进行测试)会给您与您看到的错误类似的错误。

So "This is a test string" is a const char* . 因此, "This is a test string"const char* If you reference it as a void* , something could modify the contents of void* later. 如果将其引用为void* ,则某些内容稍后可能会修改void *的内容。 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;
}

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. 尤其是,错误无关与constTest * const pStruct ,因为这不仅意味着pStruct不得进行指向别的。 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 函数定义上的const修饰符使其成为常量,但指针指向的不是

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? 考虑到我对const正确性的理解,我不理解的是为什么编译器会发出此错误,即“从'const void *'到'void *'的无效转换”? 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? 由于函数定义上的const修饰符使它成为常数,而指针指向的不是,所以为什么这会成为问题?

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. 因为字符串文字是char const[] ,所以“衰减”为char const * ,并且转换为非常量指针将丢失const限定符。

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 . 如果这不是错误,则可以使用ptr隐式绕过iconst限定符。 C++ simply does not allow this. C ++根本不允许这样做。

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而不是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
}

You should not use C-style casts in C++. 您不应该在C ++中使用C样式的强制转换。 Use one of static_cast , reinterpret_cast , dynamic_cast and const_cast to make it clear which kind of conversion should be enforced. 使用static_castreinterpret_castdynamic_castconst_cast来明确应实施哪种转换。

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. 而且,当然,即使可以在没有警告的情况下进行编译,但程序的行为是不确定的,因为您不得使用const_cast放弃最初初始化为常量的对象的常量。


Edit: I forgot about the whole char * C compatibility business. 编辑:我忘了整个char * C兼容性业务。 But this is covered in the other answers already and, to my best understanding, does not render anything in my answer incorrect. 但这已包含在其他答案中,并且据我所知,不会使我的答案有任何错误。

"blah" is an array of 5 char const . "blah"是由5个char const的数组。 In C++11 it converts implicitly to char const* . 在C ++ 11中,它隐式转换为char const* In C++03 and earlier the literal also converted implicitly to char* , for C compatibility, but that conversion was deprecated, and it was removed in C++11. 在C ++ 03和更早的版本中,该文字也隐式转换为char* ,以实现C兼容性,但是不建议使用该转换,并在C ++ 11中将其删除。

Setting 设置

void* p = "blah";    // !Fails.

you get the conversion sequence char const[5]char const*void* , where the last one is invalid and yields an error. 您将获得转换序列char const[5]char const*void* ,其中最后一个无效并产生错误。

Setting 设置

char* p = "blah";    // Compiles with C++03 and earlier.

with C++11 you get the same conversion as for void* , and an error, but with C++03 and earlier, since the source is a literal string you get char const[5]char* . 使用C ++ 11,您将获得与void*相同的转换,但会发生错误,但是使用C ++ 03及更早版本,由于源是文字字符串,您将获得char const[5]char*

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM