I am trying to write a function template that takes a universal reference as parameter so it can accept both lvalue reference and rvalue reference.
When I write this:
template <typename Type>
concept is_number = std::integral<Type> || std::floating_point<Type>;
template <typename Type1, typename Type2>
requires is_number<Type1> && is_number<Type2>
[[nodiscard]] inline auto add(Type1&& num1, Type2&& num2) noexcept
{
return num1 + num2;
}
const std::int64_t num1 { 50 };
const double num2 { 30.6 };
std::cout << add(num1, num2) << std::endl;
The code works as expected. The add() function template successfully deduced as
[[nodiscard]] inline double add(const std::int64_t& num1, const double& num2);
However, when I try this code:
template <typename Type>
requires is_number<Type>
[[nodiscard]] inline auto absolute(Type&& number) noexcept
{
return number < 0 ? -(number) : number;
}
const double num3 { -12.5 };
std::cout << absolute(num3) << std::endl; // Compiler warns: no matching overloaded function found
Am I missing something here?
Universal references will make templates deduce reference types. This means that Type
will probably be std::int64_t&
. This is required for forwarding references to work.
Now your check is checking if the type is an integral or floating point. The type it receives is std::int64_t&
or float const&
, and both types are not numbers. They are references
To make it work you need to remove qualifiers when using your concept:
template <typename Type>
requires is_number<std::remove_cvref_t<Type>>
[[nodiscard]] inline auto absolute(Type&& number) noexcept
{
return number < 0 ? -(number) : number;
}