代码之家  ›  专栏  ›  技术社区  ›  Xirema

通过临时使用“const T&”可以解决哪些问题?

  •  0
  • Xirema  · 技术社区  · 9 年前

    例如,如果我编写的代码如下所示:

    const int & const_value = 10;
    

    与看起来像这样的代码相比,我有什么优势:

    int value = 10;
    

    const int const_value = 10;
    

    甚至对于更复杂的例子,比如

    const enterprise::complicated_class_which_takes_time_to_construct & obj = factory.create_instance();
    

    由于使用了复制省略号,以下两个代码片段的速度不应该显著加快,也不应该显著减少内存消耗。

    enterprise::complicated_class_which_takes_time_to_construct obj = factory.create_instance();
    

    const enterprise::complicated_class_which_takes_time_to_construct obj = factory.create_instance();
    

    我有没有遗漏一些东西来解释这个结构的使用,或者我没有考虑到的一个隐藏的好处?

    编辑:

    @nwp提供的答案提供了工厂方法的示例,如下所示:

    using cc = enterprise::complicated_class_which_takes_time_to_construct;
    struct Factory{
        const cc &create_instance(){
            static const cc instance;
            return instance;
        }
    } factory;
    

    虽然这是一个很好的例子,说明为什么您需要 const reference 一般来说,它不能回答为什么处理临时文件需要它,或者为什么这个特殊的构造比把临时文件放在一个合适的堆栈分配对象中要好,在正常情况下,编译器应该仍然能够复制elide。

    3 回复  |  直到 5 年前
        1
  •  2
  •   nwp    9 年前

    想象一下,工厂是“聪明”的,并且做了一些类似的事情

    using cc = enterprise::complicated_class_which_takes_time_to_construct;
    struct Factory{
        const cc &create_instance(){
            static const cc instance;
            return instance;
        }
    } factory;
    

    并进一步假设 cc 不可复制,则 const & 版本有效,而其他两个版本无效。对于实现缓存并保留本地 map 对象,您将获得对地图内对象的引用。

    编辑:

    编码指南可能会说 通过从函数中获取返回值 常量(&C); ,因为它普遍适用于值和引用。

    cc foo();
    cc &foo();
    const cc &foo();
    

    如果您使用 const cc &var = foo(); 在所有情况下,它都不会有不必要的副本。有时你会得到一个临时的,有时你不会,但代码总是做正确的事情,让你可以自由地更改实现,而不必关心你使用的函数如何返回它的返回值。还有一个问题是 const 在这里,所以它不是所有情况下的最佳符号,但它是一个可行的设计选择。

        2
  •  2
  •   Angew is no longer proud of SO    9 年前

    脑海中浮现出一个“病理”案例:不可复制和不可移动的物体。这样一个物体 不能 按值存储,因此通过引用保存它是您唯一的机会:

    #include <iostream>
    
    struct Immovable
    {
        Immovable(int i) : i(i) {}
    
        Immovable(const Immovable&) = delete;
        Immovable(Immovable&&) = delete;
    
        const int i;
    };
    
    
    Immovable factory()
    {
        return {42};
    }
    
    
    int main()
    {
        const Immovable &imm = factory();
        std::cout << imm.i;
    }
    

    [Live example]

    在一个不那么矫揉造作的注释中,您引用了复制省略来消除“重对象”优化。但是请注意,副本省略是 可选择的 你永远不能保证编译器会这样做。将临时存储在 const & 相反地 担保 不会发生复制或移动。

        3
  •  2
  •   Community CDub    8 年前

    寿命延长规则的基本原理是已知的( because Bjarne Stroustrup, the language creator, said so )要有统一的简单规则。特别是,在函数调用中用作实际参数的临时变量的生存期将延长到完整表达式的末尾,覆盖其绑定到的任何引用的生存期 const 或本地rvalue引用,使行为的这一方面相同。


    据我所知,在C++03中有一个实际用例 invented by Petru Marginean ,用于创建未知自动推断类型的本地对象,如下所示:

    Base const& o = foo( arg1, arg2, arg3 );
    

    这在以下情况下有效 Base 是任何可能的可访问基类 foo 结果类型,因为生存期扩展机制不会切片:它会扩展整个临时对象的生存期。什么时候 食品 返回一个 T 这是一个完整的 T 对象的生存期已延长。编译器知道 T 即使程序员不一定知道 T .

    当然,在C++11和更高版本中,通常可以使用 auto :

     auto const o = foo( arg1, arg2, arg3 );
    

    通过返回值优化和/或移动,这将和Marginan的技巧一样有效,而且更清晰、更简单。


    然而,当类型 T 不可移动 不可复制,然后绑定到具有临时生命周期延长的引用,似乎是保持引用的唯一方法,这是第二个用例。


    现在,惊喜?,类型 T 完全临时对象不需要从静态已知类型派生。编译器知道您持有对 一部分 临时的。该部分是基类子对象还是其他子对象并不重要,因此可以执行以下操作:

    auto const& part = foo( arg1, arg2, arg3 ).a_part;
    

    这是第三个用例,它只是关于不为整个对象引入其他从未使用过的名称,保持代码简单。


    坚持一部分(乃至整个对象)的立场:

    C++15§12.2/5 [类别.临时]

    引用所属的临时 绑定的或作为引用绑定到的子对象的完整对象的临时对象在引用的生存期内持续存在,但…

    例外情况包括,在函数调用中用作实际参数的临时变量会一直持续到完整表达式的末尾,即比它绑定到的任何正式参数引用稍长。


    在里面 an exchange with me in the Usenet group comp.lang.c++ .
    在C++03实现中 ScopeGuard 类,它是由Petru发明的。在C++11a中 ScopeGuard公司 可以使用 std::function 汽车 在客户端代码的数据声明中。

    推荐文章