TGViewer
C++: Хроники Дурки🚑 C++: Хроники Дурки🚑 @cpp_durka · 916 subscribers
Post #51 1.31K
Некоторые вещи о С++ я знаю натурально против своей воли.

Давайте возьмем вот такие флаги компиляции


--std=c++2c -O2 -pedantic -Wall -Wextra -fsanitize=address -fsanitize=undefined


Для icc сделаем -std=c++20, потому что 2c он не знает.

Стартовый пример, который хотел показать.

Ни одного ворнинга на компиляторах не выдает.

Что в этом примере:


void foo() { // never called
if constexpr(false) { // never true
if (false) { // never true
constexpr auto call = [](auto arg) {
std::printf("called %d", arg);
};
void(B<A<tag>, decltype(call)>{});
}
}
}


Самая обычная функция, которая нигде в коде не вызывается. Внутри функции if constexpr (false) который какбы тоже не должен никогда вызваться. Я бы вообще ожидал что блок внутри выкенется из компиляции. Внутри этого блока if (false). Внутри котого лямбда (причем с аргументом и локальной переменной).

Внимание, вопрос! А можно ли как-то вызвать эту лямбду?

Оказывается да, и нам в этом помогает вот такая строчка:


void(B<A<tag>, decltype(call)>{});


Что за классы такие А и В? А вот как они объявляются:


class tag;

template<class>
struct A {
template<class>
friend constexpr auto get(A);
};

template<class K, class V>
struct B {
template<class>
friend constexpr auto get(K) { return V{}; }
};

И вуаля, теперь мы можем вызвать эту функцию вот таким кодом:


int main() {
get<tag>(A<tag>{})(42);
}


Много раз повторив добьемся того же эффекта. И ни одного ворнинга.


Да, объявив лямбду вот так:


constexpr auto call = [&](auto arg) {
std::printf("called %d", arg);
};


Получим веселую ошибку компиляции:


note: a lambda closure type has a deleted default constructor


(Да, я просто добавил `[&]`).


И я изначально, встретив нечно похожее, шел по пути усложнения кода. Потому что хотел избавиться от всех ворнингов, а, например, закомментрируем template в объявлении функции get, и хотябы gcc начнет сыпать хоть какими-то ворнингами:


template<class>
struct A {
// template<class>
friend constexpr auto get(A);
};

template<class K, class V>
struct B {
// template<class>
friend constexpr auto get(K) { return V{}; }
};



warning: friend declaration 'constexpr auto get(A< <template-parameter-1-1> >)' declares a non-template function


А icc так вообще перестанет собирать код:


internal error: assertion failed at: "func_def.c", line 1915 in scan_function_body

get(A<tag>{})(42);


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


#include <cstdio>

struct A {
friend auto get(A);
};

template<class V>
struct B {
friend auto get(A) { return V{}; }
};

void foo() { // never called
if constexpr(false) { // never true
if (false) { // never true
constexpr auto call = [](auto arg) {
std::printf("called %d", arg);
};
void(B<decltype(call)>{});
}
}
}

int main() {
get(A{})(42);
}


тоже прекрасно работает.

Вот ей богу, я эту грязь знать не хотел. 🤢
  • 🤯 16
  • ❤ 4
  • 🔥 2
  • 👍 1
  • 💊 1
More from @cpp_durka
  1. Sep 8, 2026Мошенники заставили пенсионерку из Москвы переписать квартиру на Rust
  2. Aug 31, 2026Всяко разное болезненное есть в С++, из всего болезненного одно из самых болезненных - это…
  3. Aug 24, 2026Если кто не знаком с библиотекой nlohmann/json - она прекрасна. Мои мысли о том, как должн…
  4. Aug 21, 2026#толькосвоимемы Код взят тут. if (dim == 0) idx_dim = i0; else if (dim == 1) idx_dim = i1;…
  5. Aug 17, 2026Ладно, искать баги в компиляторах весело, но недостаточно. Давайте поиграемся в чуть более…
  6. Aug 10, 2026Ладно, разумеется прошлый пост был набросом. Никогда не обновляйте компиляторы без очень с…
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 →