Pull to refresh

Comments 14

Скопирую сюда сниппет, о котором идёт речь. Или godbolt.org.
template <class> void foo() {} //1
void useFirst()
{
    foo<void>();// call 1 - instantiate "void foo<int>()"
}

template <class> void foo() requires true {} //2
void useSecond()
{
    foo<void>();// call 2 - instantiate "void foo<int>() requires true"
}

// error example: definition with same mangled name '_Z3fooIvEvv' as another definition


Выглядит как довольно специфичный баг компилятора: нужно, чтобы шаблон без ограничения инстанциировался до того, как будет объявлен шаблон с ограничением, такое в своей практике видел один или два раза. Обычно есть условный .hpp, где все шаблоны объявляются, и есть .h/.cpp, где инстанциируются и используются шаблоны из .hpp.
Конечно, досадно, что не всё работает так, как надо, но это всего лишь 1-5% случаев.

Это не "баг компилятора", а "отсутствие соглашений об ABI концептов".

мне вот интересно, если взять с++17/с++20 реализации того же pair из доклада Андрея (ссылка в статье), и сравнить, то какие цифры получатся? Там ведь весь нюанс именно в том, что реализация с концептами получается сильно проще, с меньшим числом прокси-классов

Спасибо за идею, обязательно проверим.

Пока что могу сказать, что основываясь на сравнении с optional (тоже один из примеров в статье Андрея), вариант с trailing requires clause работает значительно медленнее классической реализации с использованием прокси/базовых классов. Но, как уже обещал, обязательно проверю отдельно pair в следующей статье по концептам.
Мне кажется работает быстрее просто потому что в случае со специализациями программист вручную делает работу компилятора по инстанцированию шаблона. И хотелось бы сразу прививать хорошие код стайлы — когда концепт называется не IsBig, как будто это type trait или consteval функция, а Big, потому что речь идёт о самом типе. Это просто логично(как по мне).
+ хотелось бы увидеть в сравнении msvc, или он настолько плох, что даже в сравнение не идёт?))

И последнее — смотрел ваш доклад по концептам(по нему узнавал как вообще ими пользоваться), хотелось бы спросить, есть ли какая то разница между
image
View post on imgur.com

и попытками вызвать конструктор вместо каста? Вообще по логике вещей касты должны быть более жестким требованием, т.к. сами требуют возможности конструктора внутри своего требования. Но…
С одной стороны да, мы довольно много подсказали компилятору сами. С другой стороны мы написали довольно много кода, который компилятору нужно прочитать, к тому же добавили несколько enable_if, наследований, которые необходимо обработать и т.д. Так что тут, вроде, более выигрышным выглядит вариант с концептами. Сложно ответить «почему», не зная деталей реализации, но мы попытались)

Про концепты — соглашусь. Логичнее было бы Big, однако в угоду старых привычек назвал IsBig, каюсь, грешен:)

Касаемо msvc: его отсутствие никак не связано с ужасными/отличными результатами. Дело в том, что уже порядка трёх лет не компилируюсь msvc, вот он и не пришёл на ум сразу (компилируюсь, что не удивительно, gcc/clang на Ubuntu). А когда большую часть замеров выполнил, то пришёл к выводу, что будет справедливо сравнивать gcc/clang выполняемые на Ubuntu (WSL) с msvc на Windows (Host). Так что пока только gcc/clang. Думаю, msvc стоит протестировать обособленно, или, может, с тем же clang, но уже на windows.

Прошу прощения, не заметил дополнения с вопросом про signed_integral.
В данном примере, на мой взгляд, разницы между static_cast и вызовом конструктора нет. (Откуда пример, кстати?)
Уточните, пожалуйста, что вы понимаете под более/менее жестким требованием?
Пример из стандартной библиотеки в реализации msvc. Насчёт 1 пункта думаю компиляторы уже давно по особому обрабатывают enable if, буквально как языковую единицу, а не «инстанцировать структурку че та там делать ...» как делается это в общем виде.
А под более жестким требованием я понимаю то как обрабатываются концепты в С++20, делятся на булевы констранты или как они там и по логике булевых операций определяется какая допустим перегрузка более строгая — boolA или boolA&&boolB (тут вторая) boolA или boolA || boolB (тут первая).
В случае с static_cast/конструктор это не так очевидно и не делится на логические единицы, но по логике человеческой возможность кастить что либо является более строгим требованием, потому что оно включает в себя требование существования конструктора

Если именно применительно к концептам, то для компилятора разницы между вариантом со static_cast и вариантом с конструктором нет. Иными словами:


template<typename T>
concept SignedIntegral_v1 = std::integral<T> && T(-1) < T(0);

template<typename T>
concept SignedIntegral_v2 = std::integral<T> && static_cast<T>(-1) < static_cast<T>(0);

void foo(SignedIntegral_v1 auto const &) {}
void foo(SignedIntegral_v2 auto const &) {}

void later() {
    foo(int32_t { 1 }); // call of overloaded 'foo(int)' is ambiguous
}
У вас кажется опечатки
template typename T
void foo(IsBig auto const &) { }
template typename T ???What im doing here???
void foo(auto const &) {}
И все-таки 50-100 миллисекунд это совсем немного для компиляции файла. В реальных проектах некоторые файлы и по 5 секунд компилируются. Интересно было бы посмотреть прогресс на таких объемах. Мало ли там например сложность каких-то алгоритмов не линейная. Да и includ-ы на файл подключаются всего один раз

Согласен, но если использовать "настоящие файлы", то невозможно отследить влияние той или иной конструкции на компиляцию. Было решено для начала посмотреть на одиночные, они же синтетические, варианты, а затем протестировать что-то из реальной жизни. Например, вариант библиотеки из C++17 VS он же, но из C++20.

Only those users with full accounts are able to leave comments. Log in, please.