Не удаётся вывести тип в шаблоне
#include <source_location>
#include <iostream>
#include <string>
#include <string_view>
#include <format>
template<class... Args>
struct source_message
{
consteval source_message(std::string_view formatString, const std::source_location& _location = std::source_location::current())
: format{ formatString }
, location{ _location }
{
}
std::format_string<Args...> format;
std::source_location location;
};
template<class... Args>
void log_trace_f(const source_message<Args...>& format, Args&&... args)
{
std::cout << format.location.function_name() << " " << std::format(format.format, std::forward<Args>(args)...) << std::endl;
}
int main()
{
// log_trace_f("{}", 123); // could not deduce template argument for 'const source_message<Args...> &' from 'const char [3]'
log_trace_f({ "{}" }, 123); // int __cdecl main(void) 123
return 0;
}
Есть ли способ завести первый вариант?
Фактически от функции log_trace_f требуется компайл тайм проверка строки форматирования и дополнительный вывод std::source_location в неявном виде, интерфейс функции log_trace_f должен быть похож на std::format()
Ответы (1 шт):
Тут две проблемы.
Во-первых, когда первый аргумент не завернут в {...}, компилятор пытается вывести Args... не только из Args&&... args, но и также из const source_message<Args...>& format. И ругается, что не может это сделать.
Не буду притворяться, что понимаю, откуда такая разница в поведении (надо закапываться в стандарт), но фикс простой - первый параметр завернуть в type_identity, чтобы заблокировать вывод из него шаблонных аргументов: const std::type_identity_t<source_message<Args...>>& format.
После этого вылезает вторая ошибка:
no known conversion from 'const char[3]' to 'const std::type_identity_t<source_message<int>>' (aka 'const source_message<int>') for 1st argument
Компилятор не хочет вызывать больше одного пользовательского преобразования за раз (const char [3] -> std::string_view -> source_message<int>). Фикс - зашаблонить конструктор source_message, чтобы убрать второе преобразование:
template <std::convertible_to<std::string_view> T>
consteval source_message(T &&format, const std::source_location &location = std::source_location::current())
: format(std::forward<T>(format)), location(location)
{}