【问题标题】:const reference to temporary and copying - C++对临时和复制的 const 引用 - C++
【发布时间】:2010-03-05 04:50:48
【问题描述】:

请考虑以下代码,

struct foo
{
    foo()
    {
        std::cout << "Constructing!" << std::endl;
    }

    foo(const foo& f)
    {
        std::cout << "Copy constructing!" << std::endl;
    }

    ~foo()
    {
        std::cout << "Destructing.." << std::endl;
    }
};

foo get()
{
    foo f;
    return f;
}

int main()
{
    const foo& f = get();
    std::cout << "before return" << std::endl;
    return 0;
}

MSVC 上的输出

Constructing!
Copy constructing!
Destructing..
before return
Destructing..

GCC 的输出

Constructing!
before return
Destructing..

MSVC 上的结果看起来不正确。

问题

  1. AFAIK,GCC 在这里产生正确的结果。为什么 MSVC 会给出不同的结果以及它为什么要进行复制构建?
  2. 由于返回值优化,const foo&amp; f = get()const foo f = get() 产生相同的输出。在这种情况下,应该首选哪种写作方式?

任何想法..

【问题讨论】:

  • 那是因为 gcc 默认开启了 RVO。要关闭它,请使用 gcc 的 -fno-elide-constructors 选项。

标签: c++ return-value-optimization


【解决方案1】:

您的 MSVC 构建没有优化。打开它们,您将获得相同的输出。

默认情况下,GCC 只是在您的临时文件上执行 RVO。它基本上是在做:

const foo& f = foo();

MSVC 不是。它在函数中创建foo,将其复制到函数外部(因此复制构造函数调用),破坏内部foo,然后绑定引用。

两个输出都是正确的。 RVO 是标准明确允许程序的可观察行为发生变化的一个实例。

【讨论】:

  • 是的。我现在才发现。我更改了优化级别,它的行为相同。
  • 那么您推荐哪种写作方式? const foo&amp; f = get()const foo f = get()?
  • @Appu:嗯,可能是前者。无论如何,在通用代码中,例如,如果右侧是容器,您希望 const T&amp; 避免复制。所以为了一致性,不妨做const foo&amp;。也就是说,有些人可能会觉得这很奇怪,所以我在围栏上。
  • 感谢 GMan 和其他人的帮助...:)
  • 还可能值得指出的是,在 C++0x 中,前者允许在绑定 const 引用之前复制返回值。在 C++0x 中不允许这种复制:只要被调用的函数可以访问复制构造函数(将局部变量复制到返回值中),该行应该没问题(调用代码不再需要访问副本构造函数)。
【解决方案2】:

您看到的是return value optimization,它是copy elision 的一种。两个程序都是正确的;编译器特别被赋予了消除临时对象的选项,该临时对象仅用于将数据从一个永久对象移动到另一个对象。

【讨论】:

    【解决方案3】:

    get() 函数正在构造本地(打印构造!),并按值返回一个 Foo 对象。必须创建返回的 Foo 对象,并通过复制构造(打印复制构造!)来完成。请注意,这是分配给 main 中的 const foo & f 的对象值。

    在赋值发生之前,函数必须从 get() 返回并且局部变量(即 get() 中的 foo f;)必须被销毁。 (打印 1st Destructing..)程序从那里终止(即从 main 返回)然后由 get() 返回并分配给“f”的对象被销毁。 (打印第二次破坏...)

    您看到两个编译器输出不同的原因是 GCC 正在优化 get() 的返回值,并且只是将 const foo &amp;f = get() 替换为 const foo &amp;f = foo

    【讨论】:

      【解决方案4】:

      1) 发生这种情况是因为不同的优化策略。因为你没有 operator=,MSVC 可以将代码重组为类似 const foo& f(get()) 的东西,因此执行复制 onstructor。 2)取决于你想要达到的目标:

      const foo& f = get();
      f = get(); // Incorrect, const references cannot be reassigned.
      const foo g = get();
      g = get(); // Correct.
      

      【讨论】:

      • 我认为您完全误解了 const 修饰符的含义。 “g = get();”也是一个错误。
      • 你是对的。我的 C++ 知识在 2.5 年的测试中被毁了:(
      猜你喜欢
      • 2010-10-20
      • 1970-01-01
      • 2015-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      • 2017-07-17
      • 1970-01-01
      相关资源
      最近更新 更多