自动变量的返回值优化

4
我想知道在C++0x中,“12.8复制和移动类对象[class.copy]第31段” 中,当发生“复制省略”时,确切的操作是什么:
当满足某些条件时,实现允许省略类对象的复制/移动构造函数[...]。这种复制/移动操作的省略称为复制省略,在以下情况下被允许[...]:
- 在带有类返回类型的函数的返回语句中,当表达式是非易失自动对象的名称[...](与函数返回类型具有相同的cv-unqualified类型)时, 可以通过直接将自动对象构造到函数的返回值中来省略复制/移动操作。 - [...]
现在我想知道,如果采用以下代码,能否使复制省略生效
vector<string> gen(const char *fn) {
    if(fn == nullptr)  // this should prevent RVO
        return {"House", "Horse", "Hen"};
    vector<string> res;
    fillFromFile(res, fn);
    return res;  // copy elision possible?
}
int main() {
    vector<string> data = gen("users.dat");
}

那个规则是否不适用于这个例子,我必须明确地执行它吗?

    return move(res);  // explicitly prevent copy

请注意,我的意图是通过if来消除明显的返回值优化(RVO)。或者我完全错了吗?是否有一个涉及到returnmove的变化可以使用rvalue引用呢?
2个回答

3
是的,在这两种情况下都可以/允许进行复制省略。
在编译器术语中,这两种情况略有不同。`return {"House", "Horse", "Hen"};` 构造了一个未命名对象,因此会触发常规的返回值优化(RVO)。
`return res;` 稍微复杂一些,因为您正在返回一个早先已构造的命名对象。这种优化通常称为命名返回值优化(NRVO),编译器实现它的情况稍微少一些。
MSVC始终实现RVO,并在发布版本中执行NRVO。
我相信最近的GCC版本总是执行RVO和NRVO。
顺便说一句,我真的不明白你的 `if` 对RVO会产生什么影响。

NRVO这个术语对我来说是新的。但在此之前,它是可以进行“RVO”的:如果我通过给对象命名来构造一次对象,并在不同的路径中填充它,但始终返回预先构造的命名对象--那么RVO就会发挥作用。没有必要复制,也没有需要使用news,对吧? - towi
我对第二种情况表示怀疑。正如问题中所提到的,if条件应该防止所有形式的RVO。 - balki
@balki:为什么?我在引用的文本中没有看到任何禁止它的内容。 - jalf

0

是的,在这种情况下,编译器有特定的指令来处理res,并且res将被移动到data中。当然,编译器可以在此处轻松应用RVO / NRVO,因为它可以静态确定您从未使用nullptr调用函数,并且此外,即使无法证明,该函数也可以轻松转换,以便可以应用RVO / NRVO,最后,这甚至不会阻止RVO / NRVO,因为结果仍然可以构造。


好的,main 在这里不是一个好的例子。显然编译器可以消除我的程序的一半 :-) 但是提供两条路径,带有两个完全不同的返回值......那么这是新的吗?因为return res 可以将 res 视为一个 rval-ref,并且因此应用移动操作? - towi

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