RVO, переезд операции и дилемма - PullRequest
8 голосов
/ 21 октября 2011

Я узнал о конструкторах ходов в течение последнего дня или около того, пытаясь придерживаться общего правила возврата по значению, как кажется большинству людей, и столкнулся с интересной (для меня) дилеммой.

Предположим, что у меня есть дорогой для конструирования / копирования класс 'C', в котором правильно определены конструктор копирования, оператор присваивания, конструктор перемещения и оператор присваивания перемещения.

Во-первых, этот фрагмент кода исключает конструктор копированиякак я и ожидал:

C make_c1() {
    return C();
}

, как это:

C make_c2() {
    C tmp;
    return tmp;
}

и так (я передаю 1 или 2):

C make_c3(int a) {
    return a == 1 ? make_c1() : make_c2();
}

Когда я дохожу до этого, у меня возникает проблема:

C make_c4(int a) {
    C tmp;
    return a == 1 ? make_c1() : tmp;
}

Передача 1 запускает RVO для результата make_c1, но передача 2 запускает конструктор копирования на tmp.

Изменение функции к следующему приводит к тому, что вместо tmp запускается конструктор перемещения:

C make_c5(int a) {
    C tmp;
    return a == 1 ? make_c1() : std::move(tmp);
}

Все замечательно и замечательно, кроме ...

В этих простых примерах RVO был запущенкрасный, как я и надеялся.

Однако, что если мой код немного сложнее и на некоторых компиляторах не вызывает RVO в этой последней функции?В этом случае мне нужно будет обернуть мой вызов make_c1 в std :: move, что сделает код менее эффективным на тех компиляторах, которые вызывают RVO.

Итак, мои вопросы:

  1. Почему конструктор перемещения не вызывался в make_c4, когда я возвращал свой локальный объект?(В конце концов, он скоро будет уничтожен).
  2. В функции make_c5 я должен возвращать результаты make_c1 по значению или путем его перемещения?(Чтобы избежать разных версий кода для разных компиляторов / платформ).
  3. Есть ли лучший способ кодировать конечную функцию, чтобы она правильно работала для разумной реализации компилятора?

Компилятор, с которым я играл, это GCC 4.5.3 на Cygwin.

1 Ответ

7 голосов
/ 21 октября 2011

Неявное перемещение по возвращению является законным только в тех же контекстах, в которых RVO является законным.И RVO допустим, когда выражение является именем энергонезависимого автоматического объекта (отличного от параметра функции или оператора catch) с тем же типом cv-unqualified, что и тип возвращаемого функцией ([class.copy] / p31 / b1).

Если вы преобразуете make_c4 в:

C make_c4(int a) {
    C tmp;
    if (a == 1)
        return make_c1();
    return tmp;
}

Тогда вы получите ожидаемую конструкцию перемещения для вызова на make_c4(2).Перезапись make_c5 нежелательна именно по тем причинам, о которых вы заявляете.

Обновление:

Я должен был также включить ссылку на [expr.cond] / p6/ b1, который объясняет семантику условного выражения, когда второе выражение является prvalue, а третье - lvalue, но оба имеют одинаковый тип:

Второй и третий операнды имеют одинаковый тип;результат такого типа.Если операнды имеют тип класса, результатом является временное значение prvalue типа результата, которое инициализируется копией из второго или третьего операнда в зависимости от значения первого операнда.

Т.е. этот абзац указывает, что результирующее значение prvalue условного выражения copy-initialized из 3-го аргумента в вашем примере. Инициализация копирования определена в [dcl.init] / p14.Когда источником инициализации копирования является lvalue типа класса, это вызовет конструктор копирования типа.Если источником является rvalue, он вызовет конструктор перемещения, если таковой существует, в противном случае он вызовет конструктор копирования.

Спецификация условного выражения не допускает неявного перемещения из аргумента lvalue, дажеесли условное выражение является частью возвращаемого выражения.Вполне возможно, что язык мог быть создан для такого неявного перемещения, но, насколько я знаю, он никогда не предлагался.Кроме того, существующая спецификация условного выражения уже чрезвычайно сложна, что делает такие изменения в языке тем более сложным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...