#c #c 11
#c #c 11
Вопрос:
Возьмем, к примеру, следующий класс
#include <iostream>
using namespace std;
class A{
private:
int a_;
int b_;
A(const Aamp;) = delete;
Aamp; operator=(const Aamp;) = delete;
A(Aamp;amp;) = delete;
Aamp; operator=(Aamp;amp;) = delete;
public:
A(int a, int b) : a_{a}, b_{b}{cout<<"constructedn";}
void ephemeral() const{cout<<"operationn";}
~A(){cout<<"destructedn";}
};
A make_A(int a, int b){
return {a, b};
}
int main(){
make_A(1, 2).ephemeral();
return 0;
}
Объект создается, выполняется операция, затем он уничтожается.
Однако меня беспокоит, гарантировано ли это. Моя главная проблема заключается в том, могу ли я видеть какие-либо эффекты, о которых я не знаю, из-за свободы, предоставленной компилятору стандартом.
Я не думаю, что исключение копирования здесь является фактором, потому что все конструкторы перемещения и копирования объявлены удаленными, так как их можно вызвать?
Вызывается только тот конструктор, который принимает два целых числа. Могу ли я быть уверен, что это будет вести себя согласованно между компиляторами, платформами и уровнями оптимизации?
Я подозреваю, что ответ «да», но могут быть тонкости.
Комментарии:
1. Да, но у меня нет под рукой стандарта для цитирования
Ответ №1:
Когда return {a,b};
вы непосредственно создаете возвращаемое значение.
Никакой временный, логический или иной, не создается. Исключение не происходит.
Это возвращаемое значение доступно в возвращаемом контексте в main
. Вы можете вызвать его .ephemeral()
операцию. В конце полного выражения оно выходит за пределы области видимости, если только вы не «сохраните» его в a A constamp;
(запускается расширение срока службы ссылки) или в a Aamp;amp;
(аналогично) или в auto constamp;
autoamp;amp;
переменной or, подобной этой:
autoamp;amp; a = make_A(1, 2);
a.ephemeral();
Тем не менее, в приведенном выше случае копирование не происходит.
Ни одно из этих действий не может привести к копированию в соответствии со стандартом.
Вы правы, в некоторых случаях конструкция копирования может быть исключена из существования. Исключение — это когда два объекта объединяют свои идентификаторы и время жизни. Итак, если make_A
читать:
A make_A(int a, int b){
A r{a,b};
return r;
}
r
может быть исключено из возвращаемого значения. Здесь, однако, компилятор потребует этого A(A constamp;)
или A(Aamp;amp;)
будет определен, поэтому он не будет компилироваться с вашим A
. На практике, как только он проверяет, что они определены, он не будет вызывать их, потому r
что внутри make_A
будет исключен тот же объект, что и возвращаемое значение make_A
.
Аналогично,
A a = make_A(1,2);
временное значение, возвращаемое с помощью make_A
, должно быть таким же, как у именованной переменной a
. Исключение является временным, поэтому это также может объединить переменную внутри make_A
. В этом случае вам также необходимо A(Aamp;amp;)
или A(A constamp;)
существовать.
При удалении ctors перемещения / копирования они не могут быть вызваны, поэтому объект не может быть скопирован. Для каждого конструктора может быть вызван только один деструктор (за исключением ручного создания или уничтожения).
Если код попытается вызвать их, он сгенерирует ошибку во время компиляции.
В C 17 вы можете даже return A(a,b);
и аналогичная гарантия возникает.
Вы также можете A a = make_A(1,2);
, и возникает аналогичная гарантия.
Это описывается как «гарантированное исключение», но скорее это превращает некоторые операции в «описания того, как что-то создать».
Итак, есть случаи, когда вы сможете выполнять действия, требующие перемещения или копирования ctors в C 03 или C 11 или C 14, но в C 17 теперь сделайте что-то похожее на «elision» и больше не требуйте перемещения или копирования ctors.