Приветствую на моем канале, посвященному языку программирования C++!
В этом канале я пишу интересные факты, советы, редкие возможности языка, особенности реализации компилятора.
Для каждого поста будут свои хэштеги:
#creepy - стремные правила Стандарта
#compiler - интересные факты про компилятор и его внутренности
#advice - советы из практики, не обязательно связаны напрямую с языком
#madskillz - технические чудеса
#video - YouTube видео про C++
(список хэштегов может пополняться)
В этом канале я пишу интересные факты, советы, редкие возможности языка, особенности реализации компилятора.
Для каждого поста будут свои хэштеги:
#creepy - стремные правила Стандарта
#compiler - интересные факты про компилятор и его внутренности
#advice - советы из практики, не обязательно связаны напрямую с языком
#madskillz - технические чудеса
#video - YouTube видео про C++
(список хэштегов может пополняться)
#creepy
Alternative operator representations (диграфы)
Когда-то давным-давно на свете существовали странные кодировки, в которых не существовало базовых символов.
Например, кодировка для немецкого языка
Чтобы юзеры кодировки смогли программировать на C++, была реализована гениальная схема - в языке вместо
Вместе с заменой
Также для прикола были сделаны триграфы - можно вместо
Alternative operator representations (диграфы)
Когда-то давным-давно на свете существовали странные кодировки, в которых не существовало базовых символов.
Например, кодировка для немецкого языка
DIN 66003
, бывшая в использовании с 1974 по 1999 годы. Она была создана прямой заменой символов [\]{|}~
на ÄÖÜäöüß
, которых нет в ASCII. Получилось очень круто - никому не нужные скобочки заменили на буквы алфавита.Чтобы юзеры кодировки смогли программировать на C++, была реализована гениальная схема - в языке вместо
{
, }
, [
, ]
, #
можно писать соответственно <%
, %>
, <:
, :>
, %:
, %:%:
.Вместе с заменой
&&
на and
, !=
на not_eq
, etc. получился код, который можно скомпилировать и сейчас:int main(int argc, char* argv<::>)
<%
// lambda with reference-capture:
auto greet = <:bitand:>(const char* name)
<%
std::cout << "Hello " << name
<< " from " << argv<:0:> << '\n';
%>;
if (argc > 1 and argv<:1:> not_eq nullptr) <%
greet(argv<:1:>);
%> else <%
greet("Anon");
%>
%>
Также для прикола были сделаны триграфы - можно вместо
{
писать ??<
, вместо [
писать ??(
, и так далее.😁3👍1
#compiler
Как в компиляторе реализуют NRVO?
(и почему он не всегда работает)
Для подробной информации о явлении можно прочитать на cppreference.
Оптимизация
Даже если copy/move ctors имеют побочные эффекты, их все равно не вызовут. Эти конструкторы можно объявить
Эта оптимизация старая, все компиляторы ее делают, а с C++17 она стала обязательной при определенных условиях.
Оптимизация
Условие здесь похожее - если у нас все
Как вычисление NRVO происходит в компиляторе Clang?
Во время парсинга файла Clang держит стек scope (думаю +- понятно для чего нужны скоупы). Одни scope вложены в другие scope, и образуют дерево из scope.
Свой scope создается для каждого метода, каждого класса, каждого if-выражения, каждого for-loop, и так далее. Самый "высокоуровневый" scope это scope Translation Unit-а.
Как в компиляторе реализуют NRVO?
(и почему он не всегда работает)
Для подробной информации о явлении можно прочитать на cppreference.
NRVO (Named Return Value Optimization)
- это оптимизация из класса copy elision
. Copy elision
это отсутствие вызова конструкторов копирования/мува за счёт того, что объект изначально создается в целевом месте.Оптимизация
RVO (Return Value Optimization)
выглядит чуть проще:std::string foo1() {Компилятор может понять, что никакой нужды в вызове copy/move нет - надо заранее выделить место на стеке под
return std::string("bar");
}
std::string f = foo1();
std::string f
и создать строку туда.Даже если copy/move ctors имеют побочные эффекты, их все равно не вызовут. Эти конструкторы можно объявить
= delete
, или объявить без определения, или сделать private - это ни на что не повлияет.Эта оптимизация старая, все компиляторы ее делают, а с C++17 она стала обязательной при определенных условиях.
Оптимизация
NRVO
сложнее, но она не обязательная (в отличие от RVO
) - компилятор может сгенерировать суб-оптимальный код.Условие здесь похожее - если у нас все
return xxx;
внутри метода возвращают один и тот же xxx;
, то в таком случае тоже не происходит copy/move:std::string foo2() {Как мы видим, все
std::string y = "I'm a redundant string";
std::string x = "sample text";
if (x.size() % 2 == 0) {
return x;
}
x += "xxx";
return x;
}
std::string f = foo2(); // без копирования
return
возвращают одно и то же, поэтому NRVO сработает. Если бы у нас где-нибудь был return y;
или return std::move(x);
, то NRVO бы не сработал.Как вычисление NRVO происходит в компиляторе Clang?
Во время парсинга файла Clang держит стек scope (думаю +- понятно для чего нужны скоупы). Одни scope вложены в другие scope, и образуют дерево из scope.
Свой scope создается для каждого метода, каждого класса, каждого if-выражения, каждого for-loop, и так далее. Самый "высокоуровневый" scope это scope Translation Unit-а.
👍3