Предотвращает ли возвращение созданного по умолчанию объекта NRVO? - PullRequest
0 голосов
/ 18 декабря 2018

Предполагается, что у меня есть такая функция:

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 в первом случае.

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

В соответствии с запросом от ОП, здесь приведена адаптированная версия моего комментария

Мне на самом деле было интересно то же самое (особенно с тех пор, как elision copyстандарт не является «обязательным») , поэтому я быстро протестировал его в онлайн-компиляторе, заменив 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

Как вы можете видеть, в данном конкретном случае (побочные эффекты от создания структуры не видны по некоторому условию) , V1 вызывает конструктор перемещения, если some_condition() имеет значение false (по крайней мере, вclang и gcc, используя -std=c++11 и -O2 в Coliru )

Более того, как вы заметили , похоже, происходит то же самое поведениена -O3.

HTH

ps: при изучении возможности копирования вы можете To Abseil ToW # 11 интересно;)

0 голосов
/ 18 декабря 2018

С https://en.cppreference.com/w/cpp/language/copy_elision

При следующих обстоятельствах компиляторам разрешено, но не требуется опускать копию и перемещать (начиная с C ++ 11) конструкцию объектов класса, даже если копия/ move (начиная с C ++ 11) конструктор и деструктор имеют видимые побочные эффекты.Объекты создаются непосредственно в хранилище, где они в противном случае были бы скопированы / перемещены.Это оптимизация: даже когда это имеет место и конструктор копирования / перемещения (начиная с C ++ 11) не вызывается, он все равно должен присутствовать и быть доступным (как если бы оптимизация вообще не происходила), иначе программа не работает.Formed:

  • В операторе возврата, когда операндом является имя энергонезависимого объекта с автоматической продолжительностью хранения, который не является параметром функции или параметром предложения catch, икоторый имеет тот же тип класса (игнорируя квалификацию cv), что и тип, возвращаемый функцией.Этот вариант удаления копии известен как NRVO, «именованная оптимизация возвращаемого значения».

  • ...

Нетограничения с досрочным возвратом, поэтому обе версии являются кандидатами на NRVO.

...