首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >返回默认构造的对象会阻止NRVO吗?

返回默认构造的对象会阻止NRVO吗?
EN

Stack Overflow用户
提问于 2018-12-17 21:26:14
回答 2查看 153关注 0票数 2

如果我有这样的功能:

代码语言:javascript
复制
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?我很好奇,因为这似乎相当于以下几点:

代码语言:javascript
复制
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。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-12-18 19:20:37

按要求 by OP,下面是我的意见的改编版本

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

代码语言:javascript
复制
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()http://coliru.stacked-crooked.com/a/5e55efe46bfe32f5的V1

代码语言:javascript
复制
#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

代码语言:javascript
复制
call build_vector
default ctor
method
method
method
move ctor
dtor
end of call
dtor

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

代码语言:javascript
复制
#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

代码语言:javascript
复制
call nrvo_friendly_build_vector
default ctor
method
method
method
end of call
dtor

如您所见,在这种特殊情况下(构造结构的副作用在some_condition中是不可见的),如果V1为false (至少在clang和gcc中使用-std=c++11-O2__,在http://coliru.stacked-crooked.com/中使用-std=c++11-O2__),则调用some_condition()构造函数。

此外,作为你注意到了,同样的行为似乎也发生在-O3上。

HTH

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

票数 1
EN

Stack Overflow用户

发布于 2018-12-17 21:56:15

来自射影

在以下情况下,编译器是允许的,但不需要省略类对象的复制和移动(自C++11)构造,即使复制/移动(自C++11)构造函数和析构函数具有明显的副作用。这些对象直接构造到存储中,否则就会被复制/移动到存储中。这是一种优化:即使发生了复制/移动(因为没有调用C++11)构造函数,它仍然必须是存在的和可访问的(就好像根本没有优化一样),否则程序是错误的:

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

对于早期返回没有限制,因此这两个版本都是NRVO的候选版本。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53823211

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档