我最近一直在学习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预览
答案 0 :(得分:9)
如前所述,auto
忽略顶级cv限定符。阅读this article,了解auto
和decltype
如何运作的详细信息。
现在,即使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
答案 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限定符只会被忽略。