Здравствуйте, Sm0ke, Вы писали:
В 20 (ну и в 23) стандарте есть недосказанность, а в C++ ABI популярных компиляторов (gcc / clang / msvc), соответственно, есть БАГ, эксплуатирующий эту недосказанность.
А именно, ограничения (концепты и requires) не попадают в декорированные имена.
Из-за этого можно написать well-formed программу, в которой сам же компилятор нарушит ODR: даст двум разным функциям один и тот же символ линковки.
А потом сойдёт с ума сам (если проинлайнит) или сведёт с ума линкер.
template<class T> void foo() { printf("1"); }
template<class T> void bar() { foo<T>(); } // в этой точке видна только foo #1, поэтому она и вызовется
template<class T> void foo() requires true { printf("2"); }
template<class T> void buz() { foo<T>(); } // функция с ограничением имеет приоритет, поэтому вызовется foo #2
int main() {
bar<void>();
buz<void>();
}
// для разнообразия, попробуйте разнести объявления и определения foo
// и перетащить определения выше-ниже точки инстанцирования, то есть, main().
...думаете вы!
И это, я хочу заметить, дистилированный пример.
Не дистиллированный бахнул, к счастью, не в продакшене, а в домашней работе у студента. У меня глаза вытекли, прежде чем я локализовал проблему.
Мейнтейнеры gcc сперва два года отбивались, говоря, что это ill-formed no diagnostics required и чего вы хотите, а потом взяли паузу на год и вот буквально месяц или два назад признали, что это баг.
А поскольку этот баг затрагивает спецификацию ABI, то его исправление приведёт к поломке огромного количества уже скомпилированных библиотек.
Так что, тут хоть закапывай, хоть выкапывай стюардессу, но раньше 2026 года он исправлен не будет.
Ну и кстати, возвращаясь к исходной теме про шаблоны.
MSVC по этим граблям сходил в 1998 году, когда ещё концептов не было.
Разработчики компилятора верили, что параметры шаблона всегда влияют на сигнатуру функции. (А иначе зачем нужно было шаблон писать? Г — Логика!)
template<class T> void foo(); // тип функции - один и тот же: void()
int main() {
foo<int>();
foo<char>(); // фигушки, линкер всё равно возьмёт foo<int>
}
и исправили его в 2005 году.
А до того народ лайфхачил как мог: добавлял невидимые аргументы, засовывал функции внутрь шаблона класса.
Но микрософту было хорошо, они хозяева своей экосистемы, взяли и заявили о несовместимости MSVC 7 и 8, пересобрали все виндовые библиотеки, и готово.
А гусь со шлангом так уже не могут!