Условное перемещение или копирование присваивания в троичном операторе

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

#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в зависимости от состояния?

0

Решение

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

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

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

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

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

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

1

Другие решения

Если мы посмотрим на раздел о копировании / переносе исключения из проекта стандарта, [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). [Примечание: удаление копии может не выполняться, если то же выражение
оценивается в другом контексте. —Конечная записка]

1

По вопросам рекламы [email protected]