使用包含void *的结构的函数的const正确性和参数

时间:2014-08-15 15:19:01

标签: c++ pointers struct

我不会说我完全理解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 herehere

4 个答案:

答案 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_castreinterpret_castdynamic_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*