TGViewer
Channel Public Channel
C++: Хроники Дурки🚑

C++: Хроники Дурки🚑

@cpp_durka

Очень люблю C++, но это скорее уже стокгольмский синдром.
Постоянно нахожу способы стрельнуть себе в ногу.
Subscribers
916
Photos
6
Videos
0
Links
54
Recent Posts 20 shown
Post #78 1.01K
Мошенники заставили пенсионерку из Москвы переписать квартиру на Rust
  • 😁 85
  • 🤡 6
  • ❤ 1
  • 💯 1
Post #77 1.15K
Всяко разное болезненное есть в С++, из всего болезненного одно из самых болезненных - это friend функции. Если жизнь и рассудок дороги вам - не объявляйте такие функции вообще никогда.

С ними связаны много разных интересных, скажем так особенностей, про которые эксперты из комментариев скажут, что все логично, так и надо, и вообще, а как вы хотели иначе?

Никогда такого не было и вот опять - нашел следующую веселую приколюху.

Пусть у нас есть вот такой класс:



class A {
int x;
public:
A(int a) : x(a) {}
class B {
int y;
public:
B(int b) : y(b) {}
void f(A& a, B& b){
b.y = 1;
a.x = 1;
}
};
};


Да, странненький и убогенький, но вообще весь мой код примерно такой.

Так вот в нем есть класс, у него есть вложенный класс, и в нем есть функция, которая модифицирует объекты и своего класса, и того, в который он вложен.


void f(A& a, B& b){
b.y = 1;
a.x = 1;
}


И как мы можем видеть из ссылки выше - все прекрасно работает, и проблем ни у кого не вызывает.


int main() {
auto a = A(1);
auto b = A::B(2);

b.f(a, b);

return 0;
}


Но давайте теперь сделаем эту функцию friend и вынесем за пределы класса.


class A {
int x;
public:
A(int a) : x(a) {}
class B {
int y;
public:
B(int b) : y(b) {}
friend void f(A& a, B& b);
};
};

void f(A& a, A::B& b){
b.y = 1;
a.x = 1;
}


Милорд, у вас родился кудяблик, и эта штука перестала компилироваться:


int main() {

auto a = A(1);
auto b = A::B(2);

f(a, b);

return 0;
}


Потому что что? Потому что правильно, мы дружим только с B, а вот с A не дружим.


<source>:15:7: error: 'x' is a private member of 'A'
15 | a.x = 1;
| ^
<source>:2:9: note: implicitly declared private here
2 | int x;
|


Мне тут поступал запрос на глубину моих постов, поэтому я потратил целых 10 минут на изучение вопроса прежде чем пошел строчить гневный пост в интернеты, и нашел вот такое:


https://cplusplus.github.io/CWG/issues/3193.html

тут что-то про inhabiting the class scope, и насколько я понял, вот такая ошибка компиляции - это самовольство компиляторов, а в стандарте про это вроде как ничего внятного сказано не было... Или было, но я, в скудоумии своем, не разобрался...

Кароче, friend - это одна их тех вещей, которых я стараюсь избегать в своем коде... Рано или поздно я начну избегать всего С++
godbolt.org Compiler Explorer - C++ class A { int x; public: A(int a) : x(a) {} class B { int y; public: B(int b) : y(b) {} void f(A& a, B& b){ b.y = 1; a.x = 1; } }; }; int main() { auto a = A(1); …
  • ❤ 17
  • 😁 10
  • 👍 2
Post #76 1.27K
Если кто не знаком с библиотекой nlohmann/json - она прекрасна. Мои мысли о том, как должна выглядеть библиотека для пользователя, она начала формировать еще году в 2012. (я гне нашел подтверждений, что она тогда уже была, но по ощущениям где-то тогда и появилась)

Но речь не об этом.

Как написано в cppreference, если ключи равны, то и хеши от них равны.

```
Given the following values:
h, an object of type std::hash<Key>.
k1 and k2, objects of type Key.
All following requirements are satisfied:
- If k1 == k2 is trueh(k1) == h(k2) is also true.
- Unless std::hash<Key> is a program-defined specializationh(k1) will never throw an exception.
```

Иногда срабатывает даже если это объекты разных типов



const auto x1 = 42;
const auto x2 = 42u;

__builtin_printf("%b\n\n", x1 == x2);
__builtin_printf("%#08zx\n%#08zx\n",
std::hash<int>{}(x1),
std::hash<unsigned>{}(x2));


1

0x00002a
0x00002a


А вот если вы упакуете те же типы в обертку типа json:


const auto j1 = json{x1};
const auto j2 = json{x2};


__builtin_printf("%b\n\n", j1 == j2);
__builtin_printf("%#08zx\n%#08zx\n",
std::hash<json>{}(j1),
std::hash<json>{}(j2));


Тут уже как повезет. Конкретно для этой библиотеке не сраслось:

1

0x286fec1b50
0x286fec1b91


Потому что там есть своя специализация. И с ней это правило для hash не работает.


namespace std // NOLINT(cert-dcl58-cpp)
{

/// @brief hash value for JSON objects
/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)
{
std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
{
return nlohmann::detail::hash(j);
}
};


А потому эту штуку очень весело бывает использовать в hash-мапах, когда два одинаковых объекта могут стать разными ключами.

Сохраняем бдительность!!!!
GitHub nlohmann - Overview You may know me from my JSON library for C++. nlohmann has 45 repositories available. Follow their code on GitHub.
  • 🫡 19
  • 🥴 2
  • ❤ 1
  • 👍 1
Post #75 1.34K
#толькосвоимемы

Код взят тут.


if (dim == 0) idx_dim = i0;
else if (dim == 1) idx_dim = i1;
else if (dim == 2) idx_dim = i2;
else if (dim == 3) idx_dim = i3;

const struct htp_tensor * src = (idx_dim < src0->ne[dim]) ? src0 : src1;

uint32_t s0 = i0;
uint32_t s1 = i1;
uint32_t s2 = i2;
uint32_t s3 = i3;

if (dim == 0 && src == src1) s0 -= src0->ne[0];
if (dim == 1 && src == src1) s1 -= src0->ne[1];
if (dim == 2 && src == src1) s2 -= src0->ne[2];
if (dim == 3 && src == src1) s3 -= src0->ne[3];


Вообще по библиотеке много кода, который заставляет задумываться о вечном.
  • 🔥 14
Post #74 1.31K
Ладно, искать баги в компиляторах весело, но недостаточно.

Давайте поиграемся в чуть более интересные игры.

Вспомним, что у нас в С++ есть правило: структура не может иметь нулевой размер. Потому что у ее инстанса должен быть адрес, и здесь С++ как-то забыл про Zero Cost и все такое.

Зато С++ вспомнил про Zero Cost, когда мы примешиваем пустую структуру в наследование другой.

struct Empty1 {};
struct Empty2 {};

struct NonEmpty {
int Value;
};

struct MyClass1
:public NonEmpty
,public Empty1
,public Empty2
{
};

struct MyClass2
:public Empty1
,public NonEmpty
,public Empty2
{
};

struct MyClass3
:public Empty1
,public Empty2
,public NonEmpty
{
};


static_assert(sizeof(NonEmpty) + sizeof(Empty1) + sizeof(Empty2) == 6);

static_assert(sizeof(int) == 4);
static_assert(sizeof(MyClass1) == 4);
static_assert(sizeof(MyClass2) == 4);
static_assert(sizeof(MyClass3) == 4);

int main() {}


Вот эта штука скомпилируется. Тоесть у нас структуры без размера имеют размер, но этот размер не добавляется к размеру общей структуры.

Всвязи с чем я упоролся, и написал вот такой класс:

struct Empty {
void zero() { std::memset(this, 0, sizeof(*this)); }
};


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


struct CompressedPair : Empty {
CompressedPair(Empty e, bool x) : Empty(e), b(x) { }
bool b;
};


int main() {
auto a = CompressedPair({}, true);

__builtin_printf("%b\n", a.b);
a.zero();
__builtin_printf("%b\n", a.b);

return 0;
}


Выводит

1
0


(да, да, это UB, я знаю, прекратите. Мы тут поржать собрались или где?)

Но где-то тут в безумии зараждается идея... У нас же есть no_unique_address.

А на gcc он кладет данные в padding других структур.


#include <iostream>

template <typename T>
struct optional {
union {
char foo;
T val;
};
[[no_unique_address]] bool engaged;
};

template <typename View>
struct reverse_view {
[[no_unique_address]] optional<typename View::iterator> cache;
View base_;
};

struct MyView {
using iterator = int*;
char begin_;
};

int main() {
std::cout << "sizeof = " << sizeof(reverse_view<MyView>) << std::endl; // clang - 24, gcc - 16
std::cout << "alignof = " << alignof(reverse_view<MyView>) << std::endl;
}


И тут я очень хотел развернуться мыслью, но быстро нашел уже закрытый баг с std::expected. (половина примеров выше взята из обсуждения этого бага, потому что они нагляднее чем то, с чем работал я).

Если коротко, то методы expected могли зачищать переменные вокруг себя, если те оказывались в из padding. Потому что emplace пересоздавал внутренний объект целиком.


Но, увы, примера воспроизводящего это дело у меня нет. А после этого примера все остальное рядом меркнет. Увы.
godbolt.org Compiler Explorer - C++ struct Empty1 {}; struct Empty2 {}; struct NonEmpty { int Value; }; struct MyClass1 :public NonEmpty ,public Empty1 ,public Empty2 { }; struct MyClass2 :public Empty1 ,public NonEmpty ,public Empty2 { }; struct MyClass3 …
  • ❤ 15
  • 🔥 2
  • 💊 1
Post #73 1.29K
Ладно, разумеется прошлый пост был набросом.

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

Вот к примеру.

Вложенные лямбды с параметр паком.

#include <utility>

template <std::size_t N, std::size_t M>
concept C = requires {
[]<std::size_t... Is>(std::index_sequence<Is...>)
requires requires {
[]<std::size_t... Js>(std::index_sequence<Js...>)
requires requires { true; }
{}(std::make_index_sequence<M>{});
}
{}(std::make_index_sequence<N>{});
};

static_assert(C<2, 3>);


В clang 17 они нормально компилируются.
В clang 20 они крашат компилятор.
В clang 22 это починили и они компилируются.
В текущем транке снова ломают компилятор.


Не выходи из комнаты, не совершай ошибку. Компилятор обновляют слабаки и трусы.
godbolt.org Compiler Explorer - C++ template <std::size_t N, std::size_t M> concept C = requires { []<std::size_t... Is>(std::index_sequence<Is...>) requires requires { []<std::size_t... Js>(std::index_sequence<Js...>) requires requires { true; } {}(std::make_index_sequence<M>{});…
  • 🤡 15
  • 🦄 10
  • 😁 3
  • 🤔 2
  • ❤ 1
  • 🔥 1
  • 💩 1
  • 👾 1
Post #72 1.51K
Убедительная просьба - обновляйте компиляторы.

Помимо внедрения множества способов выстрелить себе в ногу, новые версии компиляторов чинят разные веселые баги.

Вот к примеру вот этот код:


struct S {
int x;

auto foo() {
return [*this](this auto&& self) {
__builtin_printf("%d ", x);
x = 10;
};
}
};


Возвращаемая лямбда возвращаемая из метода foo капчурит this, и потом модифицирует значение. Но что будет, если мы сделаем лямбду константной?


int main() {
S s{ 5 };
const auto l = s.foo();
l();
s.x = 15;
l();
__builtin_printf("%d\n", s.x);
}


вот тут clang18.1 нормально сработает и модифицирует константный класс. clang19.1 выдаст ошибку компиляции. А gcc15.2 упадет с segfault

https://godbolt.org/z/8qqbehqhE
godbolt.org Compiler Explorer - C++ struct S { int x; auto foo() { return [*this](this auto&& self) { __builtin_printf("%d ", x); x = 10; }; } }; int main() { S s{ 5 }; const auto l = s.foo(); l(); s.x = 15; l(); __builtin_printf("%d\n", s.x); }
  • 💊 21
  • 🤔 5
Post #71 1.39K
Я сначала подумал, что надо на такие мероприятия футболку брать с QR кодом канала на спине, пиарить канал на конфе.

А потом подумал: меня же спалят как админа этого канала... Свят-свят-свят.

P.S. новый пост зашедулен на понедельник :)
  • 😁 27
  • 🤡 4
  • ❤‍🔥 2
Post #70 1.91K
Те, кто со мной регулярно общаются, знают, что я ненавижу сраный move.

И всю move семантику. И у меня даже готовился доклад про то, как я его ненавижу, с кучей веселых примеров, которые не приехали еще сюда в дурку. Где я собрал в кучку почему именно я его ненавижу, но там получилось очень много и очень неструктурированно, поэтому доклад до сих пор в разработке.

Но вот один пример, который я выкину таки сюда.


int main() {
std::map<int, std::unique_ptr<int>> m;

m.emplace(1, std::make_unique<int>(42));

auto p = std::make_unique<int>(100);

auto [it, inserted] = m.emplace(std::make_pair(1, std::move(p)));
}


Здесь классика классика.

Мапа из чего-то в в unique_ptr. Ну кто так не делал (здесь должна быть ссылке на open-source пример, но у меня жопа горит).

Дальше мы делаем emplace.


Return value
A pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool value set to true if and only if the insertion took place.


Ну и вот второй emplace с ключом 1 вернет false.
А что же произойдет с p?

Он занулится, потому что мы его мувнули в функцию.


int main() {
std::map<int, std::unique_ptr<int>> m;

m.emplace(1, std::make_unique<int>(42));

auto p = std::make_unique<int>(100);

auto [it, inserted] = m.emplace(std::make_pair(1, std::move(p)));

std::cout << std::boolalpha;
std::cout << "inserted = " << inserted << '\n';
std::cout << "p is null = " << (p == nullptr) << '\n';
std::cout << "*it->second = " << *it->second << '\n';
}


Выводит


inserted = false
p is null = true
*it->second = 42


Тоесть в мапу значение не поместили, а из указателя оно пропало... Восхитительно.

Притом что если делать std::move "на месте" такого не происходит.


int main() {

auto p = std::make_unique<int>(100);

std::move(p);

std::cout << std::boolalpha;
std::cout << "p is null = " << (p.get() == nullptr) << '\n';
}



p is null = false


Да, это чинится тем, что мы убираем make_pair, но это в тестовом примере, где это легко заметить. И когда уже знаешь проблему.

Я задолбался ловить такие приколы с move.
godbolt.org Compiler Explorer - C++ int main() { std::map<int, std::unique_ptr<int>> m; m.emplace(1, std::make_unique<int>(42)); auto p = std::make_unique<int>(100); auto [it, inserted] = m.emplace(std::make_pair(1, std::move(p))); std::cout << std::boolalpha; std::cout…
  • 🤷‍♂ 20
  • 😁 11
  • 🤡 3
  • 👎 2
  • 😭 2
  • 🔥 1
Post #69 1.55K
Приятно видеть, что C++ настолько тщательно следит за обратной совместимостью.
Мы столько боли ради этой обратной совместимости пережили.

И теперь вдвойне приятно видеть, что поменяться может семантика вообще всего что угодно.

Например, вот это:


decltype(auto) f(int&& x)
{
return (x);
}

static_assert(std::same_as<decltype(f(1)), int&>);



Оно нормально скомпилируется в С++20, но упадет на static_assert в C++23. Тоесть не просто перестанет компилироваться, а тихой сапой скомпилируется "в другое".

Насколько понимаю, связано вот с этим:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.html

Но меня уже разоблачили, плюсовик я не настоящий и гениальности очередного замысла могу не понять. Как хорошо, что у меня канал не образовательный а развлекательный 🙂
godbolt.org Compiler Explorer - C++ decltype(auto) f(int&& x) { return (x); } static_assert(std::same_as<decltype(f(1)), int&>); int main(){}
  • 😁 21
  • ❤ 5
  • 🥴 2
  • 🔥 1
  • 🗿 1
Post #68 1.57K
Вместо дурки я сегодня продолжаю пересматривать доклад великолепного Константина Владимирова.

Жду не дождусь, когда он появится в открытом доступе, чтобы поскидывать интересные таймкоды. Это будет легко, просто скриптом каждые две минуты написать....

Вместо дурки хочу поделиться интересной конструкцией, о которой до этого не знал, но почерпнул из того доклада.


Пусть у нас есть класс:


template<typename T>
struct S {
S(T) {
std::cout << "S(T)" << std::endl;
}
template<typename Iter>
S(Iter beg, Iter end) {
(beg); (end);
std::cout << "S(Iter beg, Iter end)" << std::endl;
}
};


Если мы напишем что-то такое:


int a = 3;
auto s1 = S(a);

static_assert(std::same_as<decltype(s1),
S<int>>
);


То оно нормально скомпилируется.


А вот если мы попробуем как-то тригернуть второй конструктор:


std::vector<double> v;

auto s2 = S(v.begin(), v.end());


То ожидаемо упадем с ошибкой, смысл которой сводится к тому, что компилятор не знает, какой именно тип выводить.


error: no viable constructor or deduction guide for deduction of template arguments of 'S'


Что довольно логично.

Но оказывается, есть возможность указать "хинт" для того, какой тип должен выводить конструктор класса!


template<typename Iter>
S(Iter, Iter)
-> S<typename std::iterator_traits<Iter>::value_type>;


И тогда вот это компилируется:
https://godbolt.org/z/MaPo7PsP4


std::vector<double> v;

auto s2 = S(v.begin(), v.end());

static_assert(std::same_as<decltype(s2),
S<double>>
);


У Константина дальше в докладе было всякое, какие у этого есть побочные эффекты. Посмотрите доклад потом сами.

Много примеров есть прямо вот тут:
https://eel.is/c%2B%2Bdraft/over.match.class.deduct

А меня просто понесло. Мне так нравятся типы, которые меняются при каждом копировании, кто бы знал (и да, весь пост написан только ради этой ржаки):
https://godbolt.org/z/4EMnjWsza



#include <concepts>

struct Charmander {};
struct Charmeleon {};
struct Charizard {};

template<typename Form>
struct Pokemon {
Pokemon(Form) {}

template<typename OtherForm>
Pokemon(const Pokemon<OtherForm>&) {}
};


Pokemon(const Pokemon<Charmander>&)
-> Pokemon<Charmeleon>;

Pokemon(const Pokemon<Charmeleon>&)
-> Pokemon<Charizard>;

Pokemon(const Pokemon<Charizard>&)
-> Pokemon<Charmander>;


int main() {
Pokemon p1 = Charmander{};
Pokemon p2 = p1;
Pokemon p3 = p2;
Pokemon p4 = p3;

static_assert(std::same_as<
decltype(p1),
Pokemon<Charmander>
>);

static_assert(std::same_as<
decltype(p2),
Pokemon<Charmeleon>
>);

static_assert(std::same_as<
decltype(p3),
Pokemon<Charizard>
>);

static_assert(std::same_as<
decltype(p4),
Pokemon<Charmander>
>);
}
Telegram C++ and other lectures Всем привет. Кто идёт на C++ Russia из моих уважаемых подписчиков, обратите пожалуйста внимание на изменения в программе, внесённые в последний момент. https://cppconf.ru/schedule/table/#day-2 Теперь мой доклад открывает конференцию в субботу утром, а Антон…
  • 😁 29
  • 👌 4
  • ❤ 3
  • 🤯 2
Post #67 1.65K
Ой, я когда-нибудь брошу этот сраный язык.

Давайте разберем по шагам.



int main() {
Box b;
b->hi();
}


Что тут происходит? Мы создаем объект типа Box, иииии... вызываем у него оператор ->, а у того, что он вернет вызывает функцию hi(). Логично? Логично!

Смотрим, что такое у нас тип Box.


struct Box {
Proxy operator->() {
return {};
}
};


Ага, значит оператор выдает экземпляр объекта типа Proxy, и вот у него вызывается функция hi(). Логично? Логично.

Смотрим, что такое структура Proxy.


struct Proxy {
Leaf* operator->() {
static Leaf x;
return &x;
}
};


В структуре Proxy функции hi() нет. Ошибка компиляции. Логично? А хрен там плавал.

Уберите детей от мониторов.

Вот этот код компилируется и выводит ok


#include <iostream>

struct Leaf {
void hi() { std::cout << "ok"; }
};

struct Proxy {
Leaf* operator->() {
static Leaf x;
return &x;
}
};

struct Box {
Proxy operator->() {
return {};
}
};

int main() {
Box b;
b->hi();
}


Итак.

У нас С++ разворачивает оператор -> до самого конца. Например сработает даже вот это:


#include <iostream>

struct Leaf {
void hi() { std::cout << "ok"; }
};

struct Proxy4 {
Leaf* operator->() {
static Leaf x;
return &x;
}
};

struct Proxy3 {
Proxy4 operator->() {
return {};
}
};

struct Proxy2 {
Proxy3 operator->() {
return {};
}
};

struct Proxy {
Proxy2 operator->() {
return {};
}
};

struct Box {
Proxy operator->() {
return {};
}
};

int main() {
Box b;
b->hi();
}


Я вот хочу куда-нибудь в шаблонный код загнать, пранкануть, таксказать, коллег.
godbolt.org Compiler Explorer - C++ struct Leaf { void hi() { std::cout << "ok"; } }; struct Proxy { Leaf* operator->() { static Leaf x; return &x; } }; struct Box { Proxy operator->() { return {}; } }; int main() { Box b; b->hi(); }
  • 🔥 28
  • 🤯 25
  • 😁 12
  • ❤ 3
  • 💊 3
  • 🤡 1
Post #66 1.77K
Мой дорогой друг вот с этого канала о С++:

@thisnotes

Прислал мне просто восхитительную вещь.

Давайте посмотрим вот на этот код


const std::vector<Obj>& vec =
(argc > 1)
? std::vector<Obj>{}
: ready_vec;


Вот тут зашита просто удивительая проблема. Нет, это не ссылка на временный объект, потому что const продлевает время жизни ссылки.

Интересный факт, что вот такой код


#include <iostream>
#include <vector>

struct Obj {
explicit Obj() {}
Obj(const Obj&) {
std::cout << "copy\n";
}
};

int main(int argc) {

auto ready_vec = std::vector<Obj>{};
ready_vec.reserve(1);
ready_vec.emplace_back();

const std::vector<Obj>& vec =
(argc > 1)
? std::vector<Obj>{}
: ready_vec;

return vec.size();
}


Компилируется в 16 строк ассемблера, а вот если мы сделаем вот так:



#include <iostream>
#include <vector>

struct Obj {
explicit Obj() {}
Obj(const Obj&) {
std::cout << "copy\n";
}
};

int main(int argc) {

auto ready_vec = std::vector<Obj>{};
ready_vec.reserve(1);
ready_vec.emplace_back();

const auto empty_vec = std::vector<Obj>{};
const std::vector<Obj>& vec =
(argc > 1)
? empty_vec
: ready_vec;

return vec.size();
}


Компилируется в 6 строк ассемблерного кода.

А фокус в том, что тернарный оператор в этой строчке


const std::vector<Obj>& vec =
(argc > 1)
? std::vector<Obj>{}
: ready_vec;


должен оба аргумента конвертировать к одному типу. А временный объект к ссылке сконвертировать не получится, поэтому и второй аргумент к ссылке не может быть конвертирован, и создается временная копия вектора.

Думаю, в комментариях он подскажет, был ли этот пример найден в продуктовой задаче...
godbolt.org Compiler Explorer - C++ struct Obj { explicit Obj() {} Obj(const Obj&) { std::cout << "copy\n"; } }; int main(int argc) { auto ready_vec = std::vector<Obj>{}; ready_vec.reserve(1); ready_vec.emplace_back(); const std::vector<Obj>& vec = (argc > 1) …
  • 🤯 17
  • 🔥 9
  • 😁 2
  • 🌚 2
  • 🫡 1
Post #65 1.98K
Повторяю пропавший пост.

Он был о докладе Евгения Ерохина с прошедшего CppRussia. С моими пространными размышлениями о том, какие у него сложные зубодробильные доклады и как надо каждый слайд ставить на паузу и уходить получать доп образования, чтобы не терять контекст...

А пример, который хотел у него подрезать для этого канала был такой:


int fn(int a, int b) {
int res = 0;
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
if (a > b) { // проблемный бренч
res = std::rand();
}
res = std::max(res, a);
return res;
}


Утверждается, что это пример из реальной жизни, а постановка четырех NoOperation смещает операцию так, что branch prediction работает сильно лучше и значительно улучшает производительность.

Вот такая она оптимизация в 2026 году....

P.S. Если я кому-то кидал текст изначального поста - дайте знать. Там было сильно больше интересного, но больно уж мне этот пример понравился.
P.P.S. А вообще его доклад (и другие доклады) рекомендую к просмотру хотябы из чуства мазохизма. Если у кого-то есть иллюзии, что он шарит в IT - самое время разочароваться в собственных знаниях :)
  • 😭 23
  • 👏 5
  • ⚡ 1
Post #64 2K
Сорян, я выяснил, что у меня давно не было публикаций... А у меня просто один пост пропал. Не могу его найти ни в отложках ни в опубликованных...

Такчто сейчас перетасую расписание и пошел искать.
  • 🌚 21
  • ❤ 4
  • 🤝 2
Post #63 2.06K
В разных новых (уже скоро будет 10 лет) стандартах иногда бывают мелкие но очень приятные изменения.

Вот такой код

#include <iostream>

int main() {
bool b = true;

b++;

std::cout << b;
}


Был валиден до С++17, но


<source>:6:5: error: use of an operand of type 'bool' in 'operator++' is forbidden in C++17


Вот так вот.
godbolt.org Compiler Explorer - C++ int main() { bool b = true; b++; std::cout << b; }
  • ❤ 19
  • 👍 9
Post #62 2.39K
Вот сколько я дурки повидал (у меня канал про это целый заведен так-то !!!) но с удивлением я осознал, что вот такая штука


%:include <iostream>

int main() <%
int a<:3:> = <% 10, 20, 30 %>;
std::cout << a<:1:>;
%>


Компилируется во всех комипляторах...

Потому что язык заботливо сохранил диграфы — на случай, если ваша клавиатура из 1973 года.

Пипец какая жесть.
godbolt.org Compiler Explorer - C++ %:include <iostream> int main() <% int a<:3:> = <% 10, 20, 30 %>; std::cout << a<:1:>; %>
  • 😁 29
  • 🤯 15
  • 😐 2
Post #61 2.25K
Разбираем письма читателей.


Нам прислали вот такой вот код:



#include <memory>
#include <iostream>

namespace user {

struct user_type {};

using my_type = std::shared_ptr<user_type>;

void tie(my_type const&, my_type const&)
{
std::cout << "user::tie\n";
}

void oups()
{
my_type t1;
my_type t2;

tie(t1, t2);
}

} // namespace user

int main()
{
user::oups();
}


Что в нем примечательного. Под gcc/clang у нас в консоль ничего не запишется.


Program returned: 0


Для MSVC x64 запишется


Program returned: 0
user::tie


А для MSVC x86 вообще случится страшное:


Program returned: 3221225595


Ну и чтобы не оставлять предложку совсем уж без изменений, я добавлю от себя немного дурки. Если в вызов функции добавить скобки:


void oups()
{
my_type t1;
my_type t2;

(tie)(t1, t2);
}


То, внезапно, в gcc/clang мы тоже будем печатать строчку. А вот в MSVC x86 все еще будет возвращаться ненеулевой код....
godbolt.org Compiler Explorer - C++ namespace user { struct user_type {}; using my_type = std::shared_ptr<user_type>; void tie(my_type const&, my_type const&) { std::cout << "user::tie\n"; } void oups() { my_type t1; my_type t2; tie(t1, t2); } } // namespace user int…
  • 🤯 29
  • ❤ 5
  • 😱 3
Post #60 1.78K
Сегодняшняя рубрика называется "обычный шаблонный код, который компилируется только после жертвоприношения".

Что выведет вот этот код?

cpp 
#include <iostream>
#include <vector>

template <class T>
struct LoggedVector : std::vector<T> {
void Dump() const {
if (empty()) {
std::cout << "empty\n";
return;
}
std::cout << "size = " << size() << '\n';
}
};

int main() {
LoggedVector<int> v;
v.Dump();
}



Иииииии... Правильный отвееееет.....


Да, вы правы, он ничего не выведет.
Упадет на ошибке компиляции:
```
error: use of undeclared identifier 'empty'
```


Небольшой отступ чтобы код под спойлером не бросался в глаза



Что тут не так.

На самом деле надо делать или так:


void Dump() const {
if (this->empty()) {
std::cout << "empty\n";
return;
}
std::cout << "size = " << this->size() << '\n';
}


Или вот так:


using std::vector<T>::size;
using std::vector<T>::empty;


Ты literally видишь перед собой size() и empty(), они вот там, в базовом классе, рукой подать.

Но компилятор такой:

Нет.
В шаблонах я сначала притворяюсь, что базового класса почти не существует.


Особенно приятно при большом рефакторинге, когда меняешь не-шаблонный класс на шаблонный, а он потом в произвольных местах кода ломается...
godbolt.org Compiler Explorer - C++ template <class T> struct LoggedVector : std::vector<T> { void Dump() const { if (empty()) { std::cout << "empty\n"; return; } std::cout << "size = " << size() << '\n'; } }; int main() { LoggedVector<int>…
  • ❤ 29
  • 🍌 1
Post #59 14.3K
Не могу не поделиться самым веселым, на мой взгляд, примером из доклада великолепного Константина Владимирова. Он делал анонс доклада в своем tg канале.

Пример вот такой:


template <auto T = []{}>
struct S {};

S a; S b;


В чем тут цимес. У нас T - это лямбда. И если мы таким образом определяем переменные, то в тип S записываются разные лямбды, и у нас получаются два разных типа:


static_assert(
!std::is_same_v<decltype(a), decltype (b)>
);


Если же мы явно укажем пустые треугольные скобки вот так:


S<> a, b;

static_assert(
std::is_same_v<decltype(a), decltype (b)>
);


То типы, внезапно, станут одинаковыми. Ну, мы один раз объявили тип, и две переменные этого типа.


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


S a, b;


Давайте вы попробуете угадать?

Ставя треугольные скобки мы исключаем вывод типов. Мы явно указываем, какой тип мы используем.
Но если у нас есть вывод типов, у нас компиляторы начинают вести себя по-разному.

clang падает с ошибкой

```
error: template arguments deduced as 'S<(lambda at <source>:4:20){}>' in declaration of 'a' and deduced as 'S<(lambda at <source>:4:20){}>' in declaration of 'b'
7 | S a, b;
```


А gcc считает, что это два разных типа.

```
static_assert(
!std::is_same_v<decltype(a), decltype (b)>
);
```

Пруф.


Вцелом доклад Константина был просто прекрасным, и я, наверное, понатырю сюда еще примеров из его доклада через пару месяцев. А когда он выйдет в открытый доступ - обязательно дам ссылку. Я был просто в восторге от дурки, которую он показывал.
Telegram C++ and other lectures Всем привет. Кто идёт на C++ Russia из моих уважаемых подписчиков, обратите пожалуйста внимание на изменения в программе, внесённые в последний момент. https://cppconf.ru/schedule/table/#day-2 Теперь мой доклад открывает конференцию в субботу утром, а Антон…
  • 👍 18
  • 🤯 11
  • ❤ 3
  • 🔥 2
Older posts →

About this channel

How can I read @cpp_durka without a Telegram account?
TGViewer shows the public web preview Telegram publishes for C++: Хроники Дурки🚑: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does C++: Хроники Дурки🚑 have?
C++: Хроники Дурки🚑 (@cpp_durka) has 916 subscribers on Telegram, refreshed roughly every 30 minutes.
Does C++: Хроники Дурки🚑 know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →