2

假设我有一个类似的功能:

using std::vector;

vector<int> build_vector(int n)
{
   if (some_condition(n)) return {};

   vector<int> out;

   for(int x : something())
   {
      out.push_back(x);
   }

   return out;
}

return {}函数开头的是否会阻止 NRVO?我很好奇,因为这似乎等同于以下内容:

using std::vector;

vector<int> nrvo_friendly_build_vector(int n)
{
   vector<int> out;

   if (some_condition(n)) return out;

   for(int x : something())
   {
      out.push_back(x);
   }

   return out;
}

但我不清楚在第一种情况下是否允许编译器执行 NRVO。

4

2 回答 2

2

来自https://en.cppreference.com/w/cpp/language/copy_elision

在以下情况下,编译器允许但不需要省略类对象的复制和移动(C++11 起)构造,即使复制/移动(C++11 起)构造函数和析构函数具有可观察的一面-效果。对象直接构建到存储中,否则它们将被复制/移动到。这是一种优化:即使它发生并且没有调用复制/移动(C++11 起)构造函数,它仍然必须存在且可访问(就好像根本没有发生优化一样),否则程序会出错-形成:

  • 在 return 语句中,当操作数是具有自动存储持续时间的非易失性对象的名称时,它不是函数参数或 catch 子句参数,并且属于相同的类类型(忽略 cv 限定)函数返回类型。这种复制省略的变体被称为 NRVO,“命名返回值优化”。

  • ...

提前返回没有限制,因此两个版本都是 NRVO 的候选版本。

于 2018-12-17T21:56:15.937 回答
1

根据 OP 的要求,这是我的评论的改编版本

我实际上想知道同样的事情(特别是因为标准不“要求”复制省略)std::vector ,所以我通过替换Widget 结构在在线编译器中快速测试了它:

struct Widget
{
    int val = 0;
    Widget()              { printf("default ctor\n"); }
    Widget(const Widget&) { printf("copy ctor\n"); }
    Widget(Widget&&)      { printf("move ctor\n"); }

    Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
    Widget& operator=(Widget&&)      { printf("move assign\n"); return *this; }

    ~Widget() { printf("dtor\n"); }

    void method(int)
    {
        printf("method\n");
    }
};

V1 使用build_vector()http ://coliru.stacked-crooked.com/a/5e55efe46bfe32f5

#include <cstdio>
#include <array>
#include <cstdlib>

using std::array;

struct Widget
{
    int val = 0;
    Widget()              { printf("default ctor\n"); }
    Widget(const Widget&) { printf("copy ctor\n"); }
    Widget(Widget&&)      { printf("move ctor\n"); }

    Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
    Widget& operator=(Widget&&)      { printf("move assign\n"); return *this; }

    ~Widget() { printf("dtor\n"); }

    void method(int)
    {
        printf("method\n");
    }
};

bool some_condition(int x)
{
    return (x % 2) == 0;
}

array<int, 3> something()
{
    return {{1,2,3}};
}

Widget build_vector(int n)
{
   if (some_condition(n)) return {};

   Widget out;

   for(int x : something())
   {
      out.method(x);
   }

   return out;
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        return -1;
    }
    const int x = atoi(argv[1]);

    printf("call build_vector\n");
    Widget w = build_vector(x);
    printf("end of call\n");
    return w.val;
}

V1的输出

call build_vector
default ctor
method
method
method
move ctor
dtor
end of call
dtor

V2 使用nrvo_friendly_build_vector()http ://coliru.stacked-crooked.com/a/51b036c66e993d62

#include <cstdio>
#include <array>
#include <cstdlib>

using std::array;

struct Widget
{
    int val = 0;
    Widget()              { printf("default ctor\n"); }
    Widget(const Widget&) { printf("copy ctor\n"); }
    Widget(Widget&&)      { printf("move ctor\n"); }

    Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
    Widget& operator=(Widget&&)      { printf("move assign\n"); return *this; }

    ~Widget() { printf("dtor\n"); }

    void method(int)
    {
        printf("method\n");
    }
};

bool some_condition(int x)
{
    return (x % 2) == 0;
}

array<int, 3> something()
{
    return {{1,2,3}};
}

Widget nrvo_friendly_build_vector(int n)
{
   Widget out;

   if (some_condition(n)) return out;

   for(int x : something())
   {
      out.method(x);
   }

   return out;
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        return -1;
    }
    const int x = atoi(argv[1]);

    printf("call nrvo_friendly_build_vector\n");
    Widget w = nrvo_friendly_build_vector(x);
    printf("end of call\n");
    return w.val;
}

V2的输出

call nrvo_friendly_build_vector
default ctor
method
method
method
end of call
dtor

如您所见,在这种特殊情况下(some_condition 不会看到构造结构的副作用)some_condition() ,如果为 false ,V1 调用 move 构造函数(至少在 clang 和 gcc 中,在Coliru-std=c++11中使用and )-O2

此外,正如您所注意到的,同样的行为似乎也发生了-O3

高温高压

ps:在学习复制省略时,您可能会发现Abseil 的 ToW #11很有趣;)

于 2018-12-18T19:20:37.010 回答