CV 限定符是否会影响返回对象的复制省略?
Do CV qualifiers affect copy elision of returned objects?
这个问题的重复,但那里讨论的例子处理了一个内置类型(int
(。在我目前的工作中,我不断在代码审查中被标记为未能声明这样的函数:
std::string getName();
如:
const std::string getName();
后者对我来说似乎毫无意义,但我的一些同事认为const
限定符可能会让编译器有机会避免复制返回值(或一些类似的优化(。我在这个小测试程序上运行了gcc -s
:
#include <iostream>
#include <string>
const std::string name()
{
return "World";
}
int main()
{
std::cout << "Hello, " << name() << '!' << std::endl;
return 0;
}
如果我从name()
函数中删除const
,则生成的程序集是相同的。但是,我的同事建议const
限定符提供的优化(如果有的话(可能是特定于平台/编译器的,因此最好const
"以防万一"。我不愿意养成这个习惯,因为我还没有设法在野外找到任何代码来做到这一点。
所以我的问题是两部分:
- 上面示例中的
const
qualifer 有什么区别吗? - 如果返回的值是某些
class Foo
而不是std::string
的实例,这是否重要?
我的同事都是相当通情达理的人,并且愿意接受这样的想法,即这是一个古怪的约定,或多或少是偶然地潜入我们的代码库。但是没有人对这种特定的用法有足够的把握,以至于他们愿意说这是完全没有必要的。
我在您的示例中没有看到任何 (N(RVO。RVO,或复制/移动省略,在[class.copy]/31中是允许的,但不强制要求。以下是一些示例:
#include <iostream>
struct A
{
A(int i) { std::cout << "ctorn"; }
~A() { std::cout << "dtorn"; }
A& operator=(A const&)
{ std::cout << "copy-assignment-opn"; return *this; }
// N.B. no default move ctor will be created!
};
A foo() { return 42; }
A bar() { return A(42); }
const A cfoo() { return 42; }
const A cbar() { return A(42); }
int main()
{
std::cout << "A a(42);n";
A a(42);
std::cout << "na = foo();n";
a = foo();
std::cout << "na = bar();n";
a = bar();
std::cout << "nA b( foo() );n";
A b( foo() );
std::cout << "nA c( bar() );n";
A c( bar() );
std::cout << "nA d( cfoo() );n";
A d( cfoo() );
std::cout << "nA e( cbar() );n";
A e( cbar() );
std::cout << "ndtors following for a, b, c, d, en";
}
最新编译器的输出(在许多编译器上甚至-O0
(是:
甲(42(;克托尔a = foo((;克托尔复制分配操作多托a = 柱线((;克托尔复制分配操作多托A b( foo(( (;克托尔A c( bar(( (;克托尔A d( cfoo(( (;克托尔A e( cbar(( (;克托尔dtors 跟随 A, B, C, D, E多托多托多托多托多托
如您所见,返回类型是否const
不会影响 RVO。但是,它可以,因为它不是强制性的。因此,如果您有一个旧的/奇怪的编译器 - 测试它(或在文档中查找(。
在上面的示例中,有两种类型的 RVO:
将具有自动存储持续时间的对象复制/移动到返回值(也称为 NRVO(,例如
A foo() { A a; return a; }
[class.copy]/31 允许这样做
在具有类返回类型的函数的
return
语句中,当表达式是与函数返回类型具有相同 CV 非限定类型的非易失性自动对象(函数或 catch 子句参数除外(的名称时将临时对象的省略复制/移动到另一个对象
A a( foo() ); // only 1 ctor is called A foo() { return A(); } // no copy/move from the temporary to the return value
这是允许的
当未绑定到引用 (12.2( 的临时类对象将被复制/移动到具有相同 CV-unqualified 类型的类对象时
其中"cv-unqualified"(可能(意味着在这种优化中忽略了顶级const
合格。
在这两种情况下
std::string getName();
和
const std::string getName();
将制作一个字符串的副本。但是,只要你写(类似(:
std::string name;
...
name = getName();
如果getName()
的返回类型是 const 或不是 const 绝对没有区别,编译器将优化副本,只需将内部值直接复制到 name
变量getName
,而无需进行临时复制。
但是,如果您而是返回引用:
std::string& getName();
或
const std::string& getName();
它有很大的不同,因为第一种形式会让你做:
getName() = "Mats";
这可能不是您希望界面用户执行的操作。
对我来说,审稿人似乎混淆了这两种变体。
- 为什么C++在将一个对象复制到另一个对象时需要对这两个对象进行低级常量限定
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 了解在 C++ 中分配容器时的对象复制
- C++类对象复制构造函数和运算符=
- 复制 elision/RVO 会导致从同一对象复制/移动吗?
- 如何在循环内将大对象复制到omp任务中?
- 为什么当我添加一个不同的对象(复制构造函数中的参数)时调用复制构造函数?
- 无法将类构造函数中新创建的对象复制到 C++ 中的向量成员
- 将C++对象复制到指向新指针的指针处
- 将一个 OpenCV OutputArrayOfArrays 对象复制到另一个对象
- 选择性地禁用隐式对象复制
- 不能从文件到指针向量的对象复制
- 从派生对象复制构造
- OpenCV Mat对象复制速度更快
- 将子对象复制到父对象
- 为什么普通的 c++ 编译器不优化对象复制?
- C :将一个对象复制到构造函数中的另一个对象
- 使用命名对象复制构造函数
- 不必要的对象复制 - C++ STL
- 如何将一个类的对象复制到另一个类中