Я пытаюсь встроить следующий код C++:
__attribute__((always_inline)) static void inline compose(const char* s, std::function<void(const char *)> f) {
std::cout << s << std::endl;
f(s);
}
// --------------- Main ---------------
int main() {
// Nest three things
compose("hello world", [](const char *s) {
compose("hello again", [](const char *s) {
compose("hello third time", [](const char *s) {
return;
});
});
});
return 0;
}
Здесь мои продолжения относятся к типу std::function<void(const char*)>, и от компилятора с опцией always_inline я хотел преобразовать его в один сайт вызова, который выглядит следующим образом:
// --------------- Main ---------------
int main() {
// Nest three things
std::cout << "hello world" << std::endl;
std::cout << "hello again" << std::endl;
std::cout << "hello third time" << std::endl;
return 0;
}
Можно ли сделать это во время компиляции? Я думаю, что компилятор не может сделать это автоматически, но я думал, что препроцессор может, с constexpr и C++ 17.
Откуда вы знаете, что компилятор не может автоматически оптимизировать ваш код продолжения до производительности, эквивалентной второй версии? Вы пробовали Godbolt?
Я использую llvm и только что наблюдал ИК. Я не знаком с Godbolt.





Clang неплохо его оптимизирует. Использование std::function<> создает лишь небольшие накладные расходы, связанные со стиранием типа:
Стиль продолжения: https://godbolt.org/g/FxHSnV
Прямой стиль: https://godbolt.org/g/N1b8QC
Я использовал dummy_write(), чтобы избежать сложной сборки, созданной с помощью iostream и std::cout.
Стирание этого типа можно устранить, создав шаблон compose вместо использования std::function: https://godbolt.org/g/7QceN6
template<typename Func>
__attribute__((always_inline))
static void inline compose(const char* s, Func f) {
dummy_write(s);
f(s);
}
Отличное использование шаблонов, спасибо! Просто любопытно, почему в сборке стиля продолжения четыре вызова dummy_write?
Четвертый dummy_write, по-видимому, является частью метода _M_invoke(), связанного с реализацией std::function. На самом деле он не вызывается в main(), но не оптимизирован. Неоптимизированное использование std::function, скорее всего, вызовет _M_invoke().
Спасибо за дальнейшие объяснения!
Пометка compose как constexpr, что было бы очевидным решением, не работает, я попытался добавить constexpr к лямбдам, но безрезультатно.