I have some code that compiles with gcc or clang but causes a build error when undefined sanitizer is used with gcc only. It seems like std apply is being evaluated in a consteval context which fails because std format cannot be evaluated at compile time.
I have tried with gcc 13 and 14 (error message from 14 shown).
Is there some problem with the code? Should it compile at all or is gcc getting this wrong. Any suggestion for work arounds? code on godbolt here https://godbolt.org/z/aMfhq1jzG
#include <format>
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
template <char const *FS, typename... Args>
struct Formatter
{
static constexpr auto formatString{FS};
std::tuple<Args...> data;
Formatter(Args...args) : data{std::move(args)...} {}
std::string format() const
{
return std::apply([](const auto &...args) -> std::string
{ return std::format(formatString, args...); }, data);
}
};
int main() {
static constexpr char fs[] = "{} {} {}";
Formatter<fs, std::string, std::string, int> fmt("A", "B", 100);
std::cout << fmt.format();
return 0;
}
gcc 14.1.0 build error
<source>: In member function 'std::string Formatter<FS, Args>::format() const [with const char* FS = (& fs); Args = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int}; std::string = std::__cxx11::basic_string<char>]':
<source>:17:22: error: call to consteval function 'std::apply(_Fn&&, _Tuple&&) [with _Fn = Formatter<(& main()::fs), __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>::format() const::<lambda(const auto:38& ...)>; _Tuple = const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&]((*(UBSAN_NULL((&((const Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>*)this)->Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>::data), 2, 8), (&((const Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>*)this)->Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>::data))))' is not a constant expression
17 | return std::apply([](const auto &...args) -> std::string
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 | { return std::format(formatString, args...); }, data);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/refwrap.h:38,
from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/string:52,
from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/locale_classes.h:40,
from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/locale:41,
from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/format:47,
from <source>:1:
<source>:17:22: in 'constexpr' expansion of 'std::apply(_Fn&&, _Tuple&&) [with _Fn = Formatter<(& main()::fs), __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>::format() const::<lambda(const auto:38& ...)>; _Tuple = const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&]((*(UBSAN_NULL((&((const Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>*)this)->Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>::data), 2, 8), (&((const Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>*)this)->Formatter<(& fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>::data))))'
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/tuple:2936:31: in 'constexpr' expansion of 'std::__apply_impl(_Fn&&, _Tuple&&, index_sequence<_Idx ...>) [with _Fn = Formatter<(& main()::fs), __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>::format() const::<lambda(const auto:38& ...)>; _Tuple = const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&; long unsigned int ..._Idx = {0, 1, 2}; index_sequence<_Idx ...> = integer_sequence<long unsigned int, 0, 1, 2>]((* & std::forward<const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&>((* & __t))), (_Indices(), _Indices()))'
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/tuple:2921:27: in 'constexpr' expansion of 'std::__invoke(_Callable&&, _Args&& ...) [with _Callable = Formatter<(& main()::fs), __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>::format() const::<lambda(const auto:38& ...)>; _Args = {const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const int&}; typename __invoke_result<_Functor, _ArgTypes>::type = __cxx11::basic_string<char>]((* & std::get<0, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>((* & std::forward<const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&>((* & __t))))), (* & std::get<1, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>((* & std::forward<const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&>((* & __t))))), (* & std::get<2, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>((* & std::forward<const tuple<__cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>&>((* & __t))))))'
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/invoke.h:96:40: in 'constexpr' expansion of 'std::__invoke_impl(__invoke_other, _Fn&&, _Args&& ...) [with _Res = __cxx11::basic_string<char>; _Fn = Formatter<(& main()::fs), __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>::format() const::<lambda(const auto:38& ...)>; _Args = {const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const int&}]((* & std::forward<Formatter<(& main()::fs), __cxx11::basic_string<char, char_traits<char>, allocator<char> >, __cxx11::basic_string<char, char_traits<char>, allocator<char> >, int>::format() const::<lambda(const auto:38& ...)> >((* & __fn))), (* & std::forward<const __cxx11::basic_string<char>&>((* & __args#0))), (* & std::forward<const __cxx11::basic_string<char>&>((* & __args#1))), (* & std::forward<const int&>((* & __args#2))))'
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/invoke.h:61:36: error: 'Formatter<(& main()::fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>::format() const::<lambda(const auto:38& ...)> [with auto:38 = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int}; std::string = std::__cxx11::basic_string<char>]' called in a constant expression
61 | { return std::forward<_Fn>(__f)(std::forward<_Args>(__args)...); }
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:17:23: note: 'Formatter<(& main()::fs), std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>::format() const::<lambda(const auto:38& ...)> [with auto:38 = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int}; std::string = std::__cxx11::basic_string<char>]' is not usable as a 'constexpr' function because:
17 | return std::apply([](const auto &...args) -> std::string
| ^
<source>:18:55: error: call to non-'constexpr' function 'std::string std::format(format_string<_Args ...>, _Args&& ...) [with _Args = {const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const int&}; string = __cxx11::basic_string<char>; format_string<_Args ...> = basic_format_string<char, const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const __cxx11::basic_string<char, char_traits<char>, allocator<char> >&, const int&>]'
18 | { return std::format(formatString, args...); }, data);