Задать вопрос
@ImagineTables

Концепт проверки совместимости с шаблонным конструктором?

Пишу свой форматтер. Он состоит из двух классов: Argument и Format.

Format принимает неопределённое количество параметров типа Argument.

Argument принимает в конструктор разные типы и конвертирует их во внутренний std::wstring m_strValue. Тем самым, он играет две роли: 1) фильтр, который явно описывает допустимые типы, 2) суб-форматтер, которые умеет знаковые целые превращать в строки десятичного формата, беззнаковые — в строки с префиксом "0x", узкие и широкие строки приводит к одному виду (std::wstring) и т.д.

class Argument
{
	Argument(const std::wstring& ws);
	Argument(const wchar_t* ps);
	Argument(int i);
	Argument(unsigned int i);
//...
};


А Format делает так:

template<typename T>
concept ConvertibleToArgument = std::convertible_to<T, Argument>;

class Format
{
//...
template<ConvertibleToArgument... Args>
static void Output(const Args&... messageParts)
{
	std::initializer_list<Argument> partList { messageParts... };
	Impl(partList);
}
//...
};


concept ConvertibleToArgument проверяет, что переданный аргумент можно сконвертировать в Argument. А Impl далее склеивает подстроки.

Всё хорошо, всё работает. Но потом мне надоела простыня конструкторов в Argument, и я решил сгруппировать их по имплементации. Т.е. сделать шаблонными.

class Argument
{
public:

	// Constructor from a wide string.
	template <typename T_WideString> requires
	(
		std::is_same_v<T_WideString, const wchar_t*> ||
		std::is_same_v<T_WideString, const std::wstring&>
	)
	Argument(T_WideString wideString)
	{
		m_strValue = wideString;
	}
//...
}


Теперь при вызове Format::Output(L"Test"); компилятор не может найти подходящей перегрузки.

Вот болт: https://gcc.godbolt.org/z/hY3ePzvE6

Если совсем убрать concept ConvertibleToArgument, а функцию Output объявить так:

template<typename... Args>
static void Output(const Args&... messageParts)


То всё снова работает, но если передать неизвестный аргумент, ошибка компиляции показывает не на вызов с этим аргументом, а внутрь функции. Добавление static_assert ситуацию не улучшает:

static_assert((... && requires (const Args& arg) { Argument(arg); }),
	"Arguments must be convertible to Argument");


(Пока я вообще так и не понял, в каких ситуациях полезен static_assert из-за этого свойства показывать не источник ошибки, а внутрь реализации).

Как совместить concept, который позволяет подсветить неверные вызовы, с шаблонными конструкторами Argument?

Почему с явно описанным конструктором:

Argument(const wchar_t* s) { m_strValue = s; }

всё работает, а с шаблонным конструктором и проверкой std::is_same_v<T_WideString, const wchar_t*> — нет?
  • Вопрос задан
  • 17 просмотров
Подписаться 1 Сложный Комментировать
Помогут разобраться в теме Все курсы
  • Нетология
    Разработчик на C++
    12 месяцев
    Далее
  • Академия Эдюсон
    Разработчик игр на Unreal Engine + ИИ
    9 месяцев
    Далее
  • Stepik
    Профессия: Разработчик C++ (Junior)
    2 месяца
    Далее
Решения вопроса 1
@ImagineTables Автор вопроса
Кажется, понял.

В случае со строками как раз строгость проверки надо убавить:

template <typename T>
concept WideCompatible =
	std::convertible_to<T, const wchar_t*> ||
	std::same_as<T, std::wstring>;

template <WideCompatible T>
void Argument(T const& wideString)
{
	m_strValue = wideString;
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы