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

时间:2015-05-14 00:14:19

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

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

为了测试我理解const指针是如何工作的,我编写了一个简单的程序。我不打算发布整件事,因为它只有两部分是相关的。我有一个类具有int:

类型的const数据成员
const int tryToChangeMe;

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

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

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

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

正如我所料,由于我在尝试为 const 变量赋值时所引起的错误,程序无法编译。但是,我没有返回指向 const 的指针,我返回了 const 指向 const 的指针(至少那个&#39)我想我做了什么。所以为了测试这个,我试图将指针重新分配给一个新的内存地址,我非常有信心得到类似的编译错误:

temp = new int;

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

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

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

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

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

4 个答案:

答案 0 :(得分:9)

如前所述,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)会“T

Live demo

答案 1 :(得分:5)

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

答案 2 :(得分:4)

写作时

auto temp = rhs;

类型推导的工作原理如下:

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

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

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

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

即。指向const - const的{​​{1}}指针。指针就像任何其他变量一样,因此它的int - 将被丢弃(从技术上讲,顶级cv限定符为const并且它被丢弃),因此你最终得到const的类型被推断为

temp

即。指向const int* - const的非const指针,因此可以重新指定。如果你想强制执行int - ness,那么你必须将左侧声明为

const

Scott Meyers对该主题有excellent introduction(也可在他的Effective Modern C++书上找到,第1和第2项可免费浏览here),其中他解释了{{1}类型演绎有效。一旦你理解了这一点,理解const auto temp = myClass.test(); ^^^^^ need this 是轻而易举的,因为真正的template类型推导模仿了模板类型推导系统(auto除外)。

修改

还有一个额外的规则
auto

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

答案 3 :(得分:3)

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

<?php include '../connect.php'; $SQL=mysql_query("SELECT * FROM subscribers ORDER BY Name"); while($row=mysql_fetch_array($SQL)) { ?> <body> <div id="page-wrap"> <textarea id="header">SARIAYA CABLE NETWORK</textarea> <div id="identity"> <textarea id="address">Name: <?php echo $row['Name']; $name = $row['Name']; ?> Street: <?php echo $row['Street']; ?> Brgy/Sitio/Subd: <?php echo $row['Brgy/Sitio/Subd']; ?> </textarea> <div id="logo"> <div id="logoctr"> <a href="javascript:;" id="change-logo" title="Change logo">Change Logo</a> <a href="javascript:;" id="save-logo" title="Save changes">Save</a> | <a href="javascript:;" id="delete-logo" title="Delete logo">Delete Logo</a> <a href="javascript:;" id="cancel-logo" title="Cancel changes">Cancel</a> </div> <div id="logohelp"> <input id="imageloc" type="text" size="50" value="" /><br /> (max width: 540px, max height: 100px) </div> <img id="image" src="images\logo.png" alt="logo" style="height:7vw; width:7vw;" /> </div> </div> <div style="clear:both"></div> <div id="customer"> <textarea id="customer-title">Billing as of <?php echo date('F Y'); ?></textarea> <table id="meta"> <tr> <td class="meta-head">Account #</td> <td><textarea><?php echo $row['Account_No.']; ?></textarea></td> </tr> <tr> <td class="meta-head">Date of Billing</td> <td><textarea id="date"><?php echo date('F d Y'); ?></textarea></td> </tr> <tr> <td class="meta-head">Amount Due</td> <td><div class="due"></div></td> </tr> </table> </div> <table id="items"> <tr> <th>Date</th> <th>Monthly</th> <th>Installation</th> <th>Transfer</th> <th>Reconnection</th> <th>Extension</th> <th>Misc. fee</th> </tr> <? $SQLBill=mysql_query("SELECT * FROM billing WHERE Name='$name'"); while($subrow=mysql_fetch_array($SQLBill)){ ?> <tr class="item-row"> <td class="item-name"><div class="delete-wpr"><textarea> <?php echo $subrow['Date']; ?></textarea><a class="delete" href="javascript:;" title="Remove row">X</a></div></td> <td class="description"><textarea> <?php echo $subrow['Monthly']; ?></textarea></td> <td><textarea class="cost"><?php echo $subrow['Installation']; ?></textarea></td> <td><textarea class="qty"><?php echo $subrow['Reconnection']; ?></textarea></td> <td><span class="price"><?php echo $subrow['Transfer']; ?></span></td> <td><?php echo $subrow['Extension']; ?></td> <td><?php echo $subrow['Misc']; ?></td> </tr> <tr id="hiderow"> <td colspan="7"><a id="addrow" href="javascript:;" title="Add a row">Add a row</a></td> </tr> <tr> <td colspan="3" class="blank"> </td> <td colspan="3" class="total-line">Subtotal</td> <td class="total-value"><div id="subtotal"></div></td> </tr> <tr> <td colspan="3" class="blank"> </td> <td colspan="3" class="total-line">Total</td> <td class="total-value"><div id="total"></div></td> </tr> <tr> <td colspan="3" class="blank"> </td> <td colspan="3" class="total-line">Amount Paid</td> <td class="total-value"><textarea id="paid">$0.00</textarea></td> </tr> <tr> <td colspan="3" class="blank"> </td> <td colspan="3" class="total-line balance">Balance Due</td> <td class="total-value balance"><div class="due"></div></td> </tr> </table> <div id="terms"> <h5>Terms</h5> <textarea>NET 30 Days. Cutting of service will be made on unpaid balances after 30 days.</textarea> </div> </div> </body> <?php }//open of second php }//close of while mysql_close($local); mysql_close($network); ?>

部分
  

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

现在,模板参数dedcution N4296::7.1.6.4/7 [dcl.spec.auto]

  

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

最后N4296::14.8.2/3 [temp.deduct]

  

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

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