Rebekah
@Rebekah

Как создать wrapper для шаблонной функции?

Есть вот такая шаблонная функция для генерации случайного целого числа типа T, при условии что тип T действительно целое число
template<
	typename T,
	typename = std::enable_if_t<is_integral_v<decay_t<T>>>>
	T GetIntegralRand(T low, T high)
	{
		return low + rand() % (high - low + 1);
	} // GetIntegralRand


Есть похожая на нёе функция, только с вещественными числами
template<
	typename T,
	typename = std::enable_if_t<is_floating_point_v<decay_t<T>>>>
	T GetFloatingRand(T low, T high)
	{
		return low + (high - low) * rand() / RAND_MAX;
	} // GetFloatingRand


И есть обьект вопроса, функция которая по идее должна делегировать управление тем двум
template<
	typename T,
	typename = std::enable_if_t<(is_floating_point_v<decay_t<T>> || is_integral_v<decay_t<T>>)>>
	T GetRand(T low, T high)
	{
		return is_integral_v<T> ? GetIntegralRand(low, high) : GetFloatingRand(low, high);
	} // GetRand


Но она этого не делает
Ошибка: T Utilities::GetIntegralRand(T,T): не удается составить аргумент шаблон для "unnamed-symbol"
Ошибка: T Utilities::GetFloatingRand(T,T): не удается составить аргумент шаблон для "unnamed-symbol"


В чем проблема?
  • Вопрос задан
  • 94 просмотра
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
SFINAE [?] является очень тонким механизмом, из которого довольно легко вывалиться в ошибку трансляции. Именно такое вываливание присутствует сейчас в твоем коде.

template<
    typename T,
    typename = std::enable_if_t<(is_floating_point_v<decay_t<T>> || is_integral_v<decay_t<T>>)>
>
T GetRand(T low, T high)
{
    return is_integral_v<T> ? GetIntegralRand(low, high) : GetFloatingRand(low, high);
}

Безотносительно ошибок определения параметров шаблона, основная ошибка в этом коде находится в строчке
return is_integral_v<T> ? GetIntegralRand(low, high) : GetFloatingRand(low, high);

При такой записи вывод обоих GetIntegralRand и GetFloatingRand будет произведен вне зависимости от признаков аргумента T. Для T == int будет произведен вывод GetIntegralRand<int> и GetFloatingRand<int>. Это приведет к ошибке трансляции, потому что GetFloatingRand<int> вывести невозможно.

Как выйти из этой ситуации. Поскольку ты пользуешься inline-константами, а следовательно и C++17, для тебя доступна конструкция if-constexpr [?].
Ее суть сводится к тому, что трансляцию проходит только истинная, согласно условию, ветка кода условной конструкции.
Пример
template< typename T >
T GetRand( T low, T high )
{
    if constexpr( std::is_floating_point_v<std::decay_t<T>> )
    {
        return GetFloatingRand( low, high );
    }
    else if constexpr( std::is_integral_v<std::decay_t<T>> )
    {
        return GetIntegralRand( low, high );
    }
    else
    {
        return {};
    }
}


Но и это тебе, на самом деле, не нужно, т.к. оба твоих первых шаблона можно объединить одним именем с помощью SFINAE.
template< typename TValue >
auto GetRand( const TValue low, const TValue high ) -> std::enable_if_t<std::is_floating_point_v<std::decay_t<TValue>>, const TValue>
{
    return low + (high - low) * rand() / RAND_MAX;
}

template< typename TValue >
auto GetRand( const TValue low, const TValue high ) -> std::enable_if_t<std::is_integral_v<std::decay_t<TValue>>, const TValue>
{
    return low + rand() % (high - low + 1);
}

SFINAE в определении шаблона указывать стоит только тогда, когда шаблон не использует автоматический вывод аргументов. Только в этом случае будут полные гарантии что SFINAE не вывалится в ошибку трансляции.
Для шаблонов функций такие ограничения непрактичны. Поэтому условие нужно перенести из определения шаблона в определение результата функции, т.к. тип результата функции выводится после вывода типов параметров функции. TRT (Trailing Return Type) [?] в этом месте поможет сделать вывод типа результата более понятным.

Важным условием работы этого кода будет то, что оба шаблона должны быть взаимоисключающими по условиям SFINAE. Если шаблоны не будут взаимоисключающими, трансляция оборвется на ошибке неоднозначности вывода шаблона.
Поэтому, когда потребуется добавить еще один шаблон GetRand для еще одного типа, например для перечислений, нужно будет гарантировать что новая проверка тоже является взаимоисключающей со всеми другими вариантами шаблона GetRand.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Просто назовите обе готовые функции GetRand.
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы