【问题标题】:const correctness and parameters to functions with structs containing void*结构包含 void* 的函数的 const 正确性和参数
【发布时间】:2014-08-15 15:19:01
【问题描述】:

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

【问题讨论】:

  • char* 是特殊的,在指针转换方面有一个例外
  • 为了详细说明@quantdev,char * 被允许指向一个字符串字面量以向后兼容C。但这是一种不好的风格。在 C++ 中,您应该考虑只有 const char * 可以指向字符串文字。

标签: c++ pointers struct


【解决方案1】:

您观察到的错误与函数声明中使用的 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 模式,则会出现错误。)

【讨论】:

  • VC 实际上说“无法将 'const char [22]' 转换为 'void *' - 转换丢失限定符”。
  • @ChristianHackl 我认为那会更清楚。我跳了一个结论。我很高兴我在这里问。
【解决方案2】:

首先,您的测试类和函数只会让事情变得不必要地复杂。特别是,该错误与Test * const pStruct 中的const 无关,因为这只意味着不能让pStruct 指向其他任何东西。毕竟,用你自己的话来说:

函数定义上的 const 修饰符使得 指针是常量,但它指向的不是

这是重现问题的简化代码:

int main() {
    void *ptr = "This is a test string"; // error
}

至于你的问题,

鉴于我对 const 正确性的理解,我不明白的是, 这就是为什么编译器会发出此错误,“从 'const void*' to '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 隐式绕过iconst 限定符。 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_castreinterpret_castdynamic_castconst_cast 之一来明确应强制执行哪种转换。

在这种情况下,您会看到“关闭”编译器所带来的麻烦:

int main() {
    int const i[22] = {};
    void *ptr = const_cast<void*>(reinterpret_cast<void const *>(i));
}

当然,即使这可能在没有警告的情况下编译,程序的行为是 undefined 因为你不能使用const_cast 来抛弃最初初始化为的对象的常量常数。


编辑:我忘记了整个char * C 兼容性业务。但这已经包含在其他答案中,并且据我所知,不会使我的答案中的任何内容不正确。

【讨论】:

  • 我不想让事情复杂化。相反,我使用的是“最少量的复制代码”。你看,我将一些 C 代码带入 C++,而有问题的函数采用 sg_io_hdr_t 结构。该结构有一个void *,字符串文字被分配到该结构。谢谢你的详细回答。这很有帮助。
  • @AndrewFalanga:当然,引言并不是要批评你的问题。很抱歉,如果它以这种方式出现。
【解决方案3】:

首先,使用 C 风格的转换会破坏 const 的正确性。这是你的演员“起作用”的唯一原因。所以不要这样做。使用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;
}

【讨论】:

  • reinterpret_cast前面没有std::
【解决方案4】:

"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 字符串,您会得到 @987654331 @→char*.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多