Почему здесь называется конструктор перемещения?
Вот пример кода из викторины С++:
#include <iostream>
struct X {
X(const char *) { std::cout << 1; }
X(const X &) { std::cout << 2; }
X(X &&) { std::cout << 3; }
};
X f(X a) {
return a;
}
X g(const char * b) {
X c(b);
return c;
}
int main() {
f("hello");
g("hello");
}
Каким будет выход программы?
Я так думаю:
-
f(X a)
вызывается, и конструктор неявно преобразует const char*
в X, поэтому вывод 1
- Поскольку у нас нет объекта для хранения возвращаемого значения, возвращаемое значение отбрасывается, нет вывода
-
g(const char*)
вызывается, а X c(b)
X(const char*)
Выход 1
- Возвращаемое значение еще раз отбрасывается - без вывода
Итак, ответ 11. Ответ, который дается викторине, равен 131. Ответ, который я получаю с g++ 4.4.4-13, - 121.
Говорят, что этот код был скомпилирован с помощью этой команды:
g++ -std=c++11 -Wall -Wextra -O -pthread
Откуда взялось среднее число? И почему это может быть 3 или 2?
Ответы
Ответ 1
В теории это может печатать любые из 131
, 13313
, 1313
и 1331
. Это довольно глупо, как вопрос викторины.
-
f("hello");
:
- "hello" преобразуется во временный
X
через конструктор преобразования, печатает 1
.
- Временная
X
используется для инициализации аргумента функции, вызывает конструктор перемещения, печатает 3
. Это можно устранить.
-
X
используется для инициализации временного возвращаемого значения, вызывает конструктор перемещения, печатает 3
. Это параметр функции, поэтому разрешение не разрешено, но возврат является неявным перемещением.
-
g("hello");
- "hello" используется для построения
c
через конструктор преобразования, выводит 1
.
-
c
используется для инициализации временного возвращаемого значения, вызывает конструктор перемещения, печатает 3
. Это можно устранить.
Помните, что функции всегда должны строить то, что они возвращают, даже если оно просто отбрасывается вызывающим кодом.
Что касается печати 2
, то из-за того, что используемый вами древний компилятор не реализует правило implicit-move-when-return-a-local-variable.
Ответ 2
Копирование elision применяется к оператору return
в g
и, возможно, в другом месте. Цитируется из cppreference:
Копирование elision - единственная разрешенная форма оптимизации, которая может меняться наблюдаемые побочные эффекты. Поскольку некоторые компиляторы не выполняют копировать elision в любой ситуации, где это разрешено (например, в отладке режим), программы, которые полагаются на побочные эффекты копирования/перемещения конструкторы и деструкторы не переносятся.
Итак, к вашему примеру кода, вывод не может быть надежно предсказан в разных реализациях.