【问题标题】:How does Stroustrup take a non-const reference to a temporary?Stroustrup 如何对临时对象进行非常量引用?
【发布时间】:2012-08-10 09:57:14
【问题描述】:

在 Stroustrup 的 C++ Programming Language book(第 3 版)中,在 Numerics 章节中,他展示了以下代码 sn-p:

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)];
                         ^        ~~~~~~~~~~~~~~~~~~~~~~~~

我检查了所有在线可用的勘误表,没有任何内容涉及这个基本问题。我错过了什么吗?自从这本书出版以来,这方面的语言是否发生了变化(不太可能,因为这本书本身提到了反对非常量引用临时变量的规则)?这是怎么回事?


如果我修改函数以使用值而不是引用,例如slice_array&lt;double&gt; v_even = ...,然后这实际上编译。然而,事实证明我的本地 C++ 头文件使复制构造函数公开,而 Stroustrup 和各种在线参考(cppreference.com、cplusplus.com)声称复制构造函数是私有的。我认为这意味着这个解决方案是不可移植的。 Stroustrup 明确列出了一个带有非引用变量的代码示例并表示这会产生错误,这一事实进一步强化了这一点。


C++98 规范 (PDF) 将 slice_array&lt;T&gt; 声明为具有私有复制构造函数。到 2005 年(根据 this spec),大概是作为 C++03 的一部分,它变成了一个公共复制构造函数。

【问题讨论】:

  • "这本书出版后语言有没有变化" 参考绑定规则很老了; valarray 较新。它看起来像一个错误(由 BS)。
  • @curiousguy:这本书已经印刷了 20 次。我浏览了所有的勘误表;两次印刷对该功能进行了更改,但有趣的是,第二次更改实际上恢复了第一次。而且这两种变化都与手头的问题无关。
  • "这本书已经印刷了 20 次。" 开始收敛了?
  • also surprised me 学习 Stroustrup 并不完美,我知道。
  • @Anonymous:多么令人难以置信的冒犯、冒昧和无知。

标签: c++ reference language-lawyer


【解决方案1】:

原始代码示例似乎存在几个不同的问题,而且书中为许多运算符提供的声明也存在。

我认为“最佳”解决方案如下

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&lt;T&gt; 上的所有运算符都定义为 const,因为它们不是修改切片本身,而是修改内容。这些在书中被错误地定义为非const

【讨论】:

    【解决方案2】:

    这似乎是在勘误表中发布的(虽然链接现在已经死了)。

    尽管 google 很棒,但它显示了类似“slice_array& v_even”这样的搜索的快照

    Stroustrup:The C++ Programming 第三次印刷的勘误表语言
    www.research.att.com/~bs/3rd_printing4.html
    [缓存][分享]在 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 ...
    

    编辑:- 感谢您编辑问题凯文,这不再是错误了 我可以在 N3092 中看到明确提到(§ 26.6.1,Pg 944)

    4. 引入此类替换类型的实现应提供 附加函数和操作符如下:
    — for each 函数采用 const valarray&,相同的函数采用 应添加替换类型;
    — 对于每个函数采用两个 const valarray& 参数,相同的函数采用每个 const valarray& 和替换类型的组合应为 已添加。

    5. 特别是,实现应允许 valarray 由此类替换类型构建,并应允许分配 并将此类类型的计算分配给 valarray, slice_array, gslice_array、mask_array 和indirect_array 对象。

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

    【讨论】:

    • 该引用页面实际上是实时的here,它说将f() 更改为...并打印该函数的完全相同的副本。
    • 我认为那个东西是更新的,谷歌缓存的,在移动文档时可能是一些(Ctrl+C,Ctrl+V)错误;)
    • 请注意,已知 msvc 错误地能够对临时对象进行非常量引用。
    • @PlasmaHH:如果它不能编译有效的 C++ 程序,它就是不合规的。编译其他程序也没有什么不合规的。也就是说,我不确定这是否真的会阻止正确编译有效程序——这是一种可能性......
    • @Mehrdad:当然,您可以构造不会编译或产生错误结果的有效程序,具体取决于此“扩展”的实现方式,这在简单的重载情况下可能已经失败,但是至少你可以用 sfinae 写一些会失败的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 2019-02-05
    相关资源
    最近更新 更多