今日免费次数已满, 请开通会员/明日再来

8
请看以下代码:
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. 据我所知,GCC在这里产生了正确的结果。为什么MSVC会产生不同的结果,并且为什么它要进行复制构造?
  2. const foo& f = get()const foo f = get()由于返回值优化,产生相同的输出。在这种情况下,应该优先选择哪种写法?

有什么想法..


1
这是因为gcc默认开启了RVO。要关闭它,请使用gcc的-fno-elide-constructors选项。 - Prasoon Saurav
4个回答

11

你的MSVC构建没有启用优化。启用它们,你会得到相同的输出。

GCC默认仅对你的临时变量执行RVO。基本上是这样做的:

const foo& f = foo();

MSVC不支持RVO。它会在函数内创建foo,将其复制到函数外(因此调用了复制构造函数),销毁内部的foo,然后绑定引用。

两种输出都是正确的。RVO是标准明确允许程序的可观察行为改变的一种情况。


是的,我刚刚发现了。我改变了优化级别,它的行为完全一致。 - Navaneeth K N
那么你推荐哪种写法?const foo& f = get() 还是 const foo f = get() - Navaneeth K N
@Appu:嗯,可能是前者。无论如何,在通用代码中,如果右侧是容器,您都希望使用const T&以避免复制。因此为了保持一致,最好使用const foo&。话虽如此,有些人可能会觉得奇怪,所以我还没有下定决心。 - GManNickG
感谢GMan和其他人的帮助... :) - Navaneeth K N
1
也许值得指出的是,在C++0x中,前者允许在绑定const引用之前复制返回值。在C++0x中,这种复制是不允许的:只要被调用的函数可以访问复制构造函数(将局部变量复制到返回值中),该行就应该没问题(调用代码不再需要访问复制构造函数)。 - Johannes Schaub - litb

1
你正在看到 返回值优化,它是复制省略的一种。两个程序都是正确的;编译器特别给出了消除仅用于将数据从一个永久对象移动到另一个永久对象的临时变量的选项。

1

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

在进行该赋值之前,函数必须从get()返回,并且必须销毁局部变量(即get()中的foo f;)(打印第一个“Destructing…”)。然后程序终止(即从main返回),然后由get()返回并分配给“f”的对象被销毁(打印第二个“Destructing…”)。

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


-2

1) 这是由于不同的优化策略导致的。因为您没有operator=,MSVC可以将代码重构为类似于const foo& f(get())的形式,从而执行复制构造函数。 2) 取决于您想要实现什么:

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

2
我认为你完全误解了const修饰符的含义。"g = get();"也是一个错误。 - Ben Voigt
你说得对。我对C++的了解被2.5年的测试摧毁了 :( - Sad Developer

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