I'm trying to write a concept that instantiates a function if the template parameter is only of numeric type (integers, floats, doubles and so on) and throw an error if the type is a pointer type or a boolean type.
#include <iostream>
#include <type_traits>
#include <boost/type_index.hpp>
template<typename T>
concept NumericType = requires(T param)
{
{std::is_integral_v<T> || std::is_floating_point_v<T>};
{!std::is_same_v<bool, T>};
{std::is_arithmetic_v<decltype(param +1)>};
!std::is_pointer_v<T>;
};
But when I try to use it with pointer to int or float or double, the compiler instantiates the function and adds one to the pointer which works because of pointer arithmetic.
template<NumericType T>
T add_one(const T val)
{
return val + 1;
}
int main(){
int two{2};
int* pTwo = &two;
float three{3.1415};
std::cout << "add_one(two): " << add_one(two) << "\n";
std::cout << "add_one(&two): " << add_one(&two) << "\n";
std::cout << "add_one(pTwo): " << add_one(pTwo) << "\n";
std::cout << "add_one(&three): " << add_one(&three) << "\n";
std::cout << "add_one(true): " << add_one(true) << "\n";
return 0;
}
I was expecting the compiler to throw error for the cases where pointer or boolean types passed to add_one
function, but surprisingly it doesn't
I fail to understand what what is wrong with the definition of concept NumericType
since it contains a clause within the body of the concept definition that hints the compiler that type T
should not be of pointer or boolean type using the type trait std::pointer_v<T>
`!std::is_same_v<T, bool>. Here's a working example on compiler explorer.
The requires
clause only checks the validity of the expression and does not evaluate the value, you need to use nested requires
:
template<typename T>
concept NumericType = requires(T param)
{
requires std::is_integral_v<T> || std::is_floating_point_v<T>;
requires !std::is_same_v<bool, T>;
requires std::is_arithmetic_v<decltype(param +1)>;
requires !std::is_pointer_v<T>;
};