Stroustrup如何将非const引用传递给临时对象?

27

在Stroustrup的《C++程序设计语言》(第三版)中,数字章节中展示了以下代码片段:

void f(valarray<double>& d)
{
    slice_array<double>& v_even = d[slice(0,d.size()/2,2)];
    slice_array<double>& v_odd = d[slice(1,d.size()/2,2)];

    v_odd *= v_even;
    v_even = 0;
}

问题在于v_evenv_odd是非常量的对临时变量的引用,这是不允许的。而尝试编译此代码会产生错误:

error: non-const lvalue reference to type 'slice_array<double>' cannot bind to a temporary of type 'slice_array<double>'
    slice_array<double>& v_even = d[slice(0,d.size()/2,2)];
                         ^        ~~~~~~~~~~~~~~~~~~~~~~~~

我查看了所有可用的勘误表,没有涉及到这个根本性问题。我是否遗漏了什么?自那本书印刷以来,语言在这方面有所改变吗(不太可能,因为该书本身提到了针对临时引用的非const规则)?这里发生了什么?


如果我修改函数,使用值而不是引用,例如slice_array<double> v_even = ...,则实际上可以编译。然而,事实证明我的本地C++头文件使复制构造函数为public,而Stroustrup和各种在线参考资料(cppreference.com、cplusplus.com)声称复制构造函数为private。我认为这意味着这个解决方案不具备可移植性。这得到了加强,因为Stroustrup明确列出了一个代码示例,其中包含非引用变量,并指出这会产生错误。


C++98规范(PDF)声明slice_array<T>具有私有复制构造函数。到2005年(根据此规范),可能作为C++03的一部分,这个复制构造函数变为公共。


自从这本书印刷以来,语言在这方面有改变吗?参考绑定规则非常古老;valarray更为新近。看起来像是一个错误(由BS引起的)。 - curiousguy
@curiousguy:这本书已经印刷了20次。我查看了所有勘误表;有两个版本对这个函数进行了更改,但有趣的是,第二个更改实际上撤销了第一个更改。而且这两个更改都与问题无关。 - Lily Ballard
2
这本书已经印刷了20次。它开始收敛了吗? - curiousguy
1
我也很惊讶地发现Stroustrup并不完美,我知道。 - user541686
2
@匿名:多么令人不快、傲慢和无知啊。 - Tony Delroy
@TonyDelroy:如果问题和评论随时间变化,我不能一直跟随它们。第一个答案是针对第一个问题的,如果事情没有明确说明,答案将显示偏差。我们属于社区;希望不是某些支持“更改请求”的支持者。 - perilbrain
2个回答

9

原始代码示例存在几个不同的问题,书中为一些操作符给出的声明也有问题。

我认为“最好”的解决方案是按照以下方式进行:

void f(valarray<double>& d)
{
    const slice_array<double>& v_even = d[slice(0,d.size()/2,2)];
    const slice_array<double>& v_odd = d[slice(1,d.size()/2,2)];

    v_odd *= v_even;
    v_even = 0;
}

slice_array<T> 中的所有操作符都定义为 const,因为它们不会修改切片本身,而是其内容。书中将其定义为 非 const,这是错误的。


4

这似乎是在勘误表中发布的(尽管链接现在已经失效)。

然而,谷歌很棒,它显示了一个搜索的快照,例如“slice_array& v_even”

Stroustrup:C++编程语言第三版第三次印刷勘误表
www.research.att.com/~bs/3rd_printing4.html
[Cached][Share] 在Google+上分享。
查看帖子。
您公开+1了此内容。
撤消

void f(valarray<double>& d)
{
    slice_array<double>& v_even = d[slice(0,d.size()/2, 2)];
    slice_array<double>& v_odd  = d[slice(1,d.size()/2,2)];

    v_odd *= 2; // double ...

编辑:

感谢 Kevin 在问题中的编辑,现在已经没有错误了。我可以在 N3092 中清楚地看到(§ 26.6.1,第944页)。

4. 引入这种替换类型的实现应提供以下附加函数和运算符:
- 对于每个以 const valarray& 为参数的函数,应添加相同的以替换类型为参数的函数;
- 对于每个以两个 const valarray& 参数为参数的函数,应添加相同的以 const valarray& 和替换类型的所有组合为参数的函数。

5. 特别地,实现应允许从这种替换类型构造一个 valarray,并允许将这些类型的赋值和计算赋值分配给 valarray、slice_array、gslice_array、mask_array 和 indirect_array 对象。

此外,我的编译器目前没有任何问题(使用VS 2010),可以完美编译代码。


3
请注意,MSVC已知会错误地允许对临时对象使用非const引用。 - PlasmaHH
@PlasmaHH 不是虚假的,它是语言的扩展。 - RedX
@RedX:如果你愿意,你可以把每一个不符合标准C++的地方都称作扩展,但这并不改变它不符合标准的事实。 - PlasmaHH
1
@PlasmaHH:如果它不能编译有效的C++程序,那么它就是不兼容的。它偶然编译其他程序并不意味着它是不兼容的。话虽如此,我不确定这是否会阻止正确编译有效程序——这是有可能的... - user541686
1
@Mehrdad:当然,你可以构建一些有效的程序,但是如果这个“扩展”实现的不好,就可能出现无法编译或者产生错误结果的情况。在简单的重载情况下,可能已经失败了,但至少你可以使用sfinae编写一些东西,虽然会失效。 - PlasmaHH
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接