返回指向 const 数据成员和 'auto' 关键字的 const 指针。有点困惑



我最近一直在学习C ,而今天才引入 const 和const正确性的概念。为了更好地理解理论,我一直在编写一系列简单的程序,以确保我正确理解这个概念。我以为我理解了一切,但是当其中一个程序中使用 auto 关键字时,我似乎有点卡住了。

为了测试我了解const Pointer的工作方式,我写了一个简单的程序。我不会打扰发布整个内容,因为其中只有两个部分相关。我有一个具有INT类型的const数据成员的课程:

const int tryToChangeMe;

在此类i中,我还具有将const指针返回上述const int的成员函数:

const int* const MyClass::test()
{
    return &tryToChangeMe;
}

在我的主要功能中,我使用上述功能,使用 auto 关键字。为了测试我认为我对 const 的了解是正确的,然后尝试通过指针重新分配 trytotochangeme 变量。喜欢:

auto temp = myClass.test();
*temp = 100;

正如我所期望的那样,由于我试图将值分配给 const 变量时造成的错误,该程序不会编译。但是,我不仅将指针返回到 const ,我还将a const 指针返回到a const (至少这就是我以为我以为我做过)。因此,为了进行测试,我试图将指针重新分配到新的内存地址,非常有信心我会遇到类似的汇编错误:

temp = new int;

,但令人困惑的是,该程序没有任何问题。踏上调试器逐步揭示,肯定的是,该指针失去了其原始地址,并被分配了一个全新的地址。想知道发生了什么事,我只是偶然发生了删除 auto 关键字,然后用变量的完整类型替换它:

const int* const temp = myClass.test();

再次测试所有内容后,结果是预期的,这次我无法将指针重新分配给新地址。

所以我想我的问题是,为什么?为什么 auto 关键字允许您绕过指针的 const 预选赛?我做错了吗?

顺便说一句,我不确定它是否重要,但我正在使用Visual Studio 2015 Preview

,如前所述,auto忽略了顶部级别的CV Qualifier。阅读本文以了解autodecltype如何工作的详细信息。

现在,即使auto不忽略const,在您的情况下,temp仍然不是const,因为如果返回的类型为非类型,则返回类型上的顶级CV-Qualifiers the返回类型都被忽略。p> g 甚至通过-Wextra

产生以下警告

警告:在函数返回类型上忽略了类型预选赛[-wignored-Qualifiers]

可以使用C 14的decltype(auto)来证明这一点。与auto不同,decltype(auto)不会丢弃参考文献和顶级CV-Qualifiers。如果通过添加以下行修改示例,则代码仍将编译,证明temp不是const指针。

decltype(auto) temp = myClass.test();
static_assert(std::is_same<const int*, decltype(temp)>{}, "");

另一方面,如果test()返回具有顶级CV-Qualifier的类类型的对象,则auto仍然会丢弃const,但是decltype(auto)不会。

live demo

原因是默认情况下auto变量不是const。您返回const值的事实并不意味着必须将其分配给const变量;毕竟,该值是复制的(事件,该值是指指针)。您也可以使用明确的类型规范轻松尝试。当更改变量temp并且指针目标仍然为const时,存储在myClass中的值不会更改。

auto temp = rhs;

类型扣除的工作如下:

  • 如果rhs是参考,则忽略了参考

  • rhs的顶级cv(const-volatile) - qualifiers 也被忽略(如果您执行auto& temp = rhs;;在这种情况下,它们不忽略;在这种情况下,编译器模式匹配是类型)

在您的情况下,右侧的类型为

const int* const
           ^^^^^
           top-level cv qualifier

即。const指向const -int。指针与任何其他变量一样,因此它的const- ness会被丢弃(从技术上讲,顶级的CV预选赛为const,并且被丢弃),因此您最终会以temp的类型为

> >
const int*

即。非const指向const-int的指针,因此可以重新分配。如果要执行const-,则必须将左侧声明为

const auto temp = myClass.test();
^^^^^
need this

斯科特·迈耶斯(Scott Meyers)对该主题有一个很好的介绍(也可在他的上可用,有效的现代C 书籍,项目1和2免费在此处浏览),他在其中解释了template类型的推论如何有效。一旦理解这一点,理解auto就会变得轻而易举,因为真正的auto类型扣除非常接近模板类型扣除系统(明显的std::initializer_list<>例外)。

编辑

还有一个针对

的其他规则
auto&& temp = rhs;

但是要了解它,您需要了解转发(通用)引用的工作方式以及参考折叠的工作方式。

我将提供一些正式的解释从标准参考搜索者的这一事实的标准来看:

N4296::7.1.6.4/7 [dcl.spec.auto]

部分

如果占位符是自动类型的特定器,则推论类型为 使用模板参数扣除规则确定。

现在,模板参数defcution N4296::14.8.2/3 [temp.deduct]

[...] 8.3.5中描述的功能参数类型调整为 执行。

最后N4296::8.3.5/5 [dcl.fct]

确定每个参数的类型后,类型的任何参数 调整了" t"或"函数返回t"的"函数返回"为"指向" t"或"指针返回t"。生产后 参数类型的列表,任何顶级CV-Qualifiers修改 形成函数类型

时,会删除参数类型。

简而

最新更新