#c #diagnostics #compiler-directives #copy-elision
#c #диагностика #директивы компилятора #исключение копирования
Вопрос:
Исключение копирования — это аккуратный метод оптимизации, и в некоторых случаях полагаться на исключение копирования на самом деле может быть быстрее, чем передавать ссылки «вручную».
Итак, давайте предположим, что вы определили критический путь кода, в котором вы полагаетесь на тот факт, что исключение копирования выполняется вашим компилятором для пути кода для максимальной производительности.
Но теперь вы полагаетесь на оптимизацию компилятора.
Существует ли какой-либо (очевидно, специфичный для компилятора) способ гарантировать, что исключение копирования действительно выполнено, и заставить компилятор (или другой инструмент) сгенерировать предупреждение / ошибку, если исключение копирования не может быть выполнено?
(Я думаю о чем-то отдаленно похожем на Visual C __forceinline
, чем сгенерирует предупреждение, если функция, помеченная таким образом, не встроена компилятором.)
Комментарии:
1. С тех пор, как был задан этот вопрос и дан ответ на него, C 17 был опубликован и поддерживается в GCC, Clang и MS VC . Не могли бы вы обновить его с учетом этого?
2. @PhilMiller — не уверен, как в этом свете существенно меняется вопрос . Тем не менее, я поддержал ваш ответ. Полезная информация.
Ответ №1:
На самом деле нет, за исключением помещения assert(false);
в конструктор копирования.
В противном случае используйте свой любимый профилировщик, чтобы определить, достаточно ли быстры интересные части вашего приложения.
Комментарии:
1. Хм… идея с assert(false) в операторе копирования кажется приятной. Для контейнеров std и тому подобного, возможно, можно создать объект-прокси / оболочку, который имеет assert в своем операторе копирования.
2. Однако оператор копирования [присваивания] не влияет на передачу по значению.
3. @Mark: Вы правы. Утверждение переходит в конструктор копирования (поскольку это единственное, что мы хотим убедиться, что оно не вызывается).
4. Не могли бы вы обновить или удалить этот ответ в свете P0135, принятого в C 17?
Ответ №2:
Нет.
Но вы можете написать эквивалентный, хотя и полностью нечитаемый, код:
BigObj f()
{
BigObj x(g());
x.someMethod();
return x;
}
//...
BigObj z = f();
//...
переводится (с исключением копирования) в:
void f(BigObj* obj)
{
new(obj) BigObj(g());
obj->someMethod();
}
//...
char z[sizeof(BigObj)];
f((BigObj*)amp;z[0]);
//...
((BigObj*)amp;z[0])->~BigObj();
Но если серьезно, просто напишите свой код таким образом, чтобы компилятор мог исключить копию. Т.е. возвращать только один объект без ветвления:
BigObj f()
{
BigObj x, y;
// use x and y
if(condition)
return x;
else
return y;
// cannot be elided
}
BigObj f()
{
if(condition)
{
BigObj x;
return x;
}
else
{
BigObj y;
return y;
}
// can be elided
}
Комментарии:
1. @MSalters упомянул аккуратный метод, позволяющий возвращать разные объекты и при этом получать исключение копирования: если условие истинно, просто
swap
используйте оба и всегда возвращайте первое.2. @Xeo: пожалуйста, обратите внимание, что
swap
это был просто особый случай. В общем, вы можете преобразовать функцию с несколькими возвратами в один возврат, добавивT return_value
сверху, изменив каждый оператор return на присваиваниеreturn_value
и, наконец, вернувreturn_value
. Часто этот шаблон может быть оптимизирован дополнительно, например, путем инициализацииreturn_value
с разумным значением.
Ответ №3:
В C 1z (ожидается в 2017 году) в некоторых случаях потребуется гарантировать исключение копирования:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
Согласно общему cppreference.com поддержка функций компилятора wiki GCC 7 и Clang 4 гарантируют это.
Для оптимизации этого, к счастью, не должно потребоваться включение поддержки нового языка, поскольку это чистая оптимизация (с учетом более старых языковых стандартов).
Также разрешение конструктору копирования быть недоступным, когда применяется оптимизация, вероятно, потребует включения более нового языкового стандарта во время компиляции или использования свободного или расширенного режима, который не требует строгого соответствия (например, потенциально GCC -fpermissive
).).