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

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。

最佳答案

OP的As requested,这是my comment的改编版

我实际上在想同样的事情(尤其是因为标准不需要“复制省略”),所以我通过用Widget结构替换std::vector在在线编译器中对其进行了快速测试:

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");
    }
};

使用build_vector() V1 :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

使用nrvo_friendly_build_vector() V2 :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(至少在clang和gcc中,使用Coliru中的-std=c++11-O2,则V1调用move构造函数)

此外,与you have noticed一样,似乎在-O3中也会发生相同的行为。

高温超导

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

关于c++ - 返回默认构造对象是否可以防止NRVO?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53823211/

10-13 06:45