Условное перемещение или копирование присваивания в троичном операторе - PullRequest
0 голосов
/ 27 октября 2018

Для следующего фрагмента кода:

#include <utility>
#include <iostream>

#define C(name) (name ? name : "nullptr")
#define PP { std::cout << __PRETTY_FUNCTION__ << " : " << C(name) << '\n'; }
#define T { std::cout << __PRETTY_FUNCTION__ << " : " << C(name) << " -> " << C(rhs.name) << '\n'; }

struct A
{
    const char * name = nullptr;
    A(const char * name) : name{name} PP
    A(A && rhs) : name{std::exchange(rhs.name, nullptr)} PP
    A(const A & rhs) : name{rhs.name} PP
    A & operator = (A && rhs) { T; std::swap(name, rhs.name); return *this; }
    A & operator = (const A && rhs) { T; name = rhs.name; return *this; }
    ~A() PP
};

#include <random>

int main()
{
    std::random_device d;
    A a{"a"};
    A b{"b"};
    A c{"c"};
    std::cout << "begin\n";
    a = ((d() % 2) == 0) ? b : std::move(c);
    std::cout << "end\n";
}

возможны два следующих выхода:

A::A(const char*) : a
A::A(const char*) : b
A::A(const char*) : c
begin
A::A(A&&) : c
A& A::operator=(A&&) : a -> c
A::~A() : a
end
A::~A() : nullptr
A::~A() : b
A::~A() : c

и

A::A(const char*) : a
A::A(const char*) : b
A::A(const char*) : c
begin
A::A(const A&) : b
A& A::operator=(A&&) : a -> b
A::~A() : a
end
A::~A() : c
A::~A() : b
A::~A() : b

Возможно ли (в соответствии со Стандартом), чтобы компилятор избегал использования временного значения во время назначения копирования / перемещения с троичным оператором в вышеприведенном случае и отправлял оператору копирования или перемещения назначение значения правой части ( b или c) прямо на левую сторону (a) в зависимости от условия?

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

Если мы посмотрим на раздел об исключении копирования / перемещения из черновика стандарта, [class.copy.elision] , который охватывает, какие случаи копирования / перемещения могут быть исключены, даже если они имеют побочные эффекты. Мы не видим ни одного случая, который бы охватывал ваш пример:

При соблюдении определенных критериев реализация может опустить конструкцию копирования / перемещения класса. объект, даже если конструктор выбран для операции копирования / перемещения и / или деструктор для объекта имеют побочные эффекты. В таких случаях реализация обрабатывает источник и цель пропущенного копирования / перемещения. Операция, как просто два разных способа обращения к одному и тому же объекту. Если первый параметр выбран Конструктор - это ссылка на тип объекта, уничтожение которого происходит, когда цель был бы уничтожен; в противном случае разрушение происходит в более поздние времена, когда два объекта был бы уничтожен без оптимизации.119 Это исключение операций копирования / перемещения, называемых копией elision, разрешается при следующих обстоятельствах (которые могут быть объединены для устранения нескольких копий):

  • в операторе возврата в функции с типом возврата класса, когда выражение является именем энергонезависимый автоматический объект (кроме параметра функции или переменной, введенной объявление-исключение обработчика (13.3) с тем же типом (игнорируя квалификацию cv), что и функция возвращаемый тип, операция копирования / перемещения может быть опущена путем непосредственного создания автоматического объекта в возвращаемый объект вызова функции
  • в выражении throw (7.6.17), когда операндом является имя энергонезависимого автоматического объекта (кроме параметра функции или предложения catch), область действия которого не выходит за пределы конца самый внутренний включающий блок try (если он есть), операция копирования / перемещения из операнда в объект исключения (13.1) можно опустить, создав автоматический объект непосредственно в исключение объект
  • когда объявление-исключение обработчика исключений (раздел 13) объявляет объект того же type (за исключением cv-qualification) в качестве объекта исключения (13.1), операция копирования может быть опущена обработка объявления исключения как псевдонима для объекта исключения, если смысл программы быть неизменным, за исключением выполнения конструкторов и деструкторов для объекта, объявленного Исключение декларирование. [Примечание: не может быть перемещения из объекта исключения, потому что это всегда именующий. —Конечная записка]

Исключение копирования требуется, когда выражение оценивается в контексте, требующем постоянного выражения (7.7) и в постоянной инициализации (6.8.3.2). [Примечание: удаление копии может не выполняться, если то же выражение оценивается в другом контексте. —Конечная записка]

0 голосов
/ 27 октября 2018

Возможно ли ... чтобы компилятор избегал использования временного значения при назначении копирования / перемещения с троичным оператором

Это интересный вопрос из-за того, как вы написаликод.

В общее , да.Компилятору разрешается переставлять или исключать код при условии, что наблюдаемый результат такой же, как если бы он выполнил код .Это называется правилом «как будто».

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

Однако в вашем случае все конструкторы имеют наблюдаемое поведение, которое нельзя изменить - они испускают символы в стандартный вывод!

Так что в этом конкретном случае у компилятора нет иного выбора, кроме как следовать потоку вашего исходного кода.

...