【问题标题】:Returning a const pointer to a const data member and the 'auto' keyword. A bit confused返回指向 const 数据成员的 const 指针和 'auto' 关键字。有点迷茫
【发布时间】:2015-07-25 11:25:21
【问题描述】:

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

为了测试我是否理解 const 指针的工作原理,我编写了一个简单的程序。我不会费心发布整个事情,因为它只有两个部分是相关的。我有一个具有 int 类型的 const 数据成员的类:

const int tryToChangeMe;

在这个类中,我还有一个成员函数,它返回一个指向上述 const int 的 const 指针:

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

然后在我的主函数中调用上述函数,使用 auto 关键字。为了测试我认为我对 const 的了解是否正确,然后我尝试通过指针重新分配 tryToChangeMe 变量。像这样:

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

正如我所料,由于我在尝试为 const 变量赋值时导致的错误,程序无法编译。然而,我不只是返回一个指向 const 的指针,我返回了一个指向 constconst 指针(至少我是这么认为的)做过)。因此,为了测试这一点,我尝试将指针重新分配给一个新的内存地址,并确信我会得到类似的编译错误:

temp = new int;

但相当令人困惑的是,程序编译时没有任何问题。调试器逐步显示,果然,指针丢失了它的原始地址并被分配了一个全新的地址。想知道发生了什么,我碰巧删除了 auto 关键字并将其替换为变量的完整类型:

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

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

毕竟我想我的问题是,为什么?为什么 auto 关键字允许您绕过指针的 const 限定符?我是不是做错了什么?

顺便说一句,我不确定这是否重要,但我正在使用 Visual Studio 2015 预览版

【问题讨论】:

  • Herb Sutter 的这篇文章可能会帮助你理解:herbsutter.com/2013/06/07/…
  • 谢谢。现在我了解了顶级 const,事情变得更加清晰。

标签: c++ pointers c++11 constants auto


【解决方案1】:

如前所述,auto 忽略顶级 cv 限定符。阅读this article,详细了解autodecltype 的工作原理。

现在,即使 auto 没有忽略 const,在您的情况下,temp 仍然不会是 const,因为返回类型的顶级 cv 限定符是 ignored,如果类型是返回的是非类类型。

g++ 甚至使用-Wextra 产生以下警告

警告:在函数返回类型 [-Wignored-qualifiers] 上忽略类型限定符

这可以通过使用 C++14 的 decltype(auto) 来演示。与auto 不同,decltype(auto) 不会丢弃引用和顶级 cv 限定符。如果您通过添加以下行来修改您的示例,代码仍将编译,证明 temp 不是 const 指针。

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

另一方面,如果 test() 返回一个具有顶级 cv 限定符的类类型的对象,那么 auto 仍会丢弃 const,但 decltype(auto) 不会。

Live demo

【讨论】:

    【解决方案2】:

    原因是auto变量默认不是const。您返回 const 值的事实并不意味着它必须分配给 const 变量;毕竟这个值是复制的(尽管这个值是一个指针)。您也可以使用显式类型规范轻松尝试。 myClass 中存储的值在更改变量 temp 时不会更改,并且指针目标仍然是 const,因此仍然遵守 constness。

    【讨论】:

    • 那么,如果我理解正确,这是否意味着返回 const 指针本质上是没有意义的? (没有双关语)
    • @Pete 如果你想要一个const 指针,然后用const auto 声明它
    • @Pete 请参阅 herehere
    • @vsoftco 是的,我现在明白了。但是,如果没有 const auto 仍然尊重 constness,那么就没有真正的需要。对吗?
    • @Pete 问题是const-ness 不被auto 认可,它在执行auto temp 时被丢弃。只要您执行auto&amp; temp,cv 限定符不会被丢弃。在后一种情况下,编译器会进行模式匹配。
    【解决方案3】:

    当你写作时

    auto temp = rhs;
    

    类型推导如下:

    • 如果rhs 是引用,则忽略该引用

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

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

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

    const 指向 const-int 的指针。指针与任何其他变量一样,因此它的 const-ness 将被丢弃(从技术上讲,顶级 cv 限定符是 const 并且它被丢弃),因此您最终会推断出 temp 的类型作为

    const int*
    

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

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

    Scott Meyers 对此主题有一个excellent introduction(也可以在他的Effective Modern C++ 书中找到,第1 项和第2 项可免费浏览here),其中他解释了template 类型推断的工作原理。一旦你理解了这一点,理解auto 就轻而易举了,因为auto 类型推导非常接近模板类型推导系统(std::initializer_list&lt;&gt; 除外)。

    编辑

    还有一个额外的规则

    auto&& temp = rhs;
    

    但要了解它,您需要了解how forwarding (universal) references work and how reference collapsing works

    【讨论】:

      【解决方案4】:

      我将为标准参考搜索者提供标准中的一些正式解释。

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

      如果占位符是自动类型说明符,则推导的类型是 使用模板参数推导规则确定。

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

      [...] 8.3.5 中描述的函数参数类型调整是 执行。

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

      确定每个参数的类型后,任何类型的参数 “T的数组”或“返回T的函数”调整为“指向的指针” T”或“指向返回 T 的函数的指针”。生产后 参数类型列表,任何顶级 cv-qualifiers 修改 形成函数类型时删除参数类型

      简而言之,是的,在这种情况下,CV 限定符会被忽略。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-18
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 2021-12-08
        • 2012-11-07
        相关资源
        最近更新 更多