CV限定符会影响返回对象的复制吗?

时间:2013-10-12 19:50:30

标签: c++

这可能是this question的副本,但正在讨论的示例涉及内置类型(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;
}

如果我从const函数中删除name(),则生成的程序集是相同的。但是,我的同事们建议const限定符提供的优化(如果有的话)可能是特定于平台/编译器的,所以最好是在const'以防万一“。我不愿意采用这种习惯,因为我没有设法在野外找到任何代码。

所以我的问题是两个人:

  1. 上例中的const资格是否有所不同?
  2. 如果返回的值是某个class Foo而不是std::string的实例,会不会有问题?
  3. 我的同事们都是非常合理的人,并且乐于接受这样一个观点,即这是一个古怪的惯例,它偶然会悄悄进入我们的代码库。但是没有人对这种特殊用法有足够的肯定,他们愿意说这完全没必要。

2 个答案:

答案 0 :(得分:2)

我的示例中没有看到任何(N)RVO。 RVO或复制/移动省略是允许,但未在[class.copy] / 31中强制执行。以下是一些例子:

#include <iostream>

struct A
{
    A(int i) { std::cout << "ctor\n"; }
    ~A() { std::cout << "dtor\n"; }
    A& operator=(A const&)
    { std::cout << "copy-assignment-op\n"; 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, e\n";
}

最近编译器(即使在-O0上的许多编译器)的输出是:

A a(42);
ctor

a = foo();
ctor
copy-assignment-op
dtor

a = bar();
ctor
copy-assignment-op
dtor

A b( foo() );
ctor

A c( bar() );
ctor

A d( cfoo() );
ctor

A e( cbar() );
ctor

dtors following for a, b, c, d, e
dtor
dtor
dtor
dtor
dtor

如您所见,返回类型是否为const不会影响RVO。但是,它可以,因为它没有强制要求。所以,如果你有一个旧的/奇怪的编译器 - 测试它(或在文档中查找)。


上例中有两种RVO:

  1. 将具有自动存储持续时间的对象的elision复制/移动到返回值(也称为NRVO),例如

    A foo() { A a; return a; }
    

    [class.copy] / 31允许此

      

    在具有类返回类型的函数中的return语句中,当表达式是具有相同cv-unqualified的非易失性自动对象(函数或catch子句参数除外)的名称时键入函数返回类型

  2. 将临时文件的删除复制/移动到另一个对象

    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类型的类对象时

  3. “cv-unqualified”(可能)表示此类优化会忽略顶级const限定。

答案 1 :(得分:1)

同时

的案例
std::string getName();

const std::string getName();

会有一个字符串的副本。但是,只要你写(类似):

std::string name;
...
name = getName();

如果getName()的返回类型是const或不是const,它将绝对没有区别,编译器将优化副本并将getName内的内部值直接复制到{{1没有制作临时副本的变量。

但是,如果您返回引用:

name

std::string& getName();

它有很大的不同,因为第一种形式可以让你做到:

const std::string& getName();

这可能不是你想要界面的用户做的事情。

对我而言,审稿人似乎对这两种变体感到困惑。