Reputation: 11753
I'll use the following example to illustrate my question:
template<typename T>
T diff(T a, T b)
{
return a-b;
}
I expect this template function works only when the type T is signed. The only solution I can figure out is to use delete keyword for all the unsigned types:
template<>
unsigned char diff(unsigned char,unsigned char) == delete;
template<>
unsigned char diff(unsigned char,unsigned char) == delete;
Are there other solutions?
Upvotes: 34
Views: 5056
Reputation: 16242
I am surprised nobody answered this, which is pretty robust and reliable and IMO canonical since and after C++11.
template<typename T, class = typename std::enable_if<std::is_signed<T>::value>::type>
T diff(T a, T b)
{
return a-b;
}
in C++14 you can use:
template<typename T, class = std::enable_if_t<std::is_signed<T>::value>>
T diff(T a, T b)
{
return a-b;
}
in C++17 you can use:
template<typename T, class = std::enable_if_t<std::is_signed_v<T>>>
T diff(T a, T b)
{
return a-b;
}
This alternative could help with multiple "overloads" if necessary (I will leave it at there)
template<typename T, std::enable_if_t<std::is_signed_v<T>,int> =0>
T diff(T a, T b)
{
return a-b;
}
...and in C++20 you can use Concepts (not shown).
Upvotes: 1
Reputation: 4191
There are many good answers here, however this question comes up as a result of the search "c++ template argument signed". We are in 2022 now, so I think it makes sense to add an answer, using concepts in C++20:
#include <concepts>
#include <iostream>
template <std::signed_integral T>
T diff(T a, T b)
{
return a - b;
}
int main()
{
std::cout << diff(1U, 2U) << std::endl;
}
The compiler fails to compile the program above with the message (among others):
error: use of function ‘T diff(T, T) [with T = unsigned int]’ with unsatisfied constraints
The core language concept std::signed_integral
, used here, is described on this page.
Upvotes: 0
Reputation: 107
#include <type_traits>
template<typename T>
std::enable_if_t<(0>-T(1)),T> diff(T a, T b)
{
return a-b;
}
use (0>-T(1)) ,I assume init T for -1 will less than 0. and unsigned value not possible less than 0
Upvotes: -1
Reputation: 6471
What does your program expect as a result? As it stands, you return an unsigned as a result of a difference. IMHO, this is a bug waiting to happen.
#include <type_trait>
template<typename T>
auto diff(T&& a, T&& b)
{
static_assert (std::is_unsigned<T>::value);
return typename std::make_signed<T>::type(a - b);
}
A more modern wait to write this:
inline auto diff(const auto a, const auto b)
{
static_assert ( std::is_unsigned<decltype(a)>::value
&& std::is_unsigned<decltype(b)>::value );
return typename std::make_signed<decltype(a -b)>::type(a - b);
}
[edit] I feel the need to add this comment: using unsigned integral types in math equations is always tricky. The example above would be a very useful add-on to any math package, if real-life situations, you often have to resort to casting to make the result of differences signed
, or the math doesn't work.
Upvotes: 2
Reputation: 275385
So there are a few issues I have with your function.
First, your function requires all 3 types to match -- the left, right and result types. So signed char a; int b; diff(a-b);
won't work for no good reason.
template<class L, class R>
auto diff( L l, R r )
-> typename std::enable_if<
std::is_signed<L>::value && std::is_signed<R>::value,
typename std::decay<decltype( l-r )>::type
>::type
{
return l-r;
}
the second thing I'd want to do is make a diff object; you cannot easily pass your diff
function around, and higher order functions are awesome.
struct diff_t {
template<class L, class R>
auto operator()(L l, R r)const
-> decltype( diff(l,r) )
{ return diff(l,r); }
};
Now we can pass diff_t{}
to an algorithm, as it holds the "overload set" of diff
in one (trivial) C++ object.
Now this is serious overkill. A simple static_assert
can also work.
The static_assert
will generate better error messages, but won't support other code using SFINAE to see if diff
can be called. It will simply generate a hard error.
Upvotes: 2
Reputation: 3677
How about static assert
with std::is_signed
?
template<typename T>
T diff(T a, T b)
{
static_assert(std::is_signed<T>::value, "signed values only");
return a-b;
}
See it live there : http://ideone.com/l8nWYQ
Upvotes: 36
Reputation: 1987
I would use static_assert
with a nice error message. enable_if
will only get your IDE in trouble and fail to compile with a message like
identifier
diff
not found
which doesn't help much.
So why not like this:
#include <type_traits>
template <typename T>
T diff(T a, T b)
{
static_assert(std::is_signed< T >::value, "T should be signed");
return a - b;
}
that way, when you invoke diff
with something else than a signed type, you will get the compiler to write this kind of message:
error: T should be signed
with the location and the values to the call to diff
and that's exactly what you're looking for.
Upvotes: 11
Reputation: 6440
You can use std::is_signed
together with std::enable_if
:
template<typename T>
T diff(T a, T b);
template<typename T>
std::enable_if_t<std::is_signed<T>::value, T> diff(T a, T b) {
return a - b;
}
Here std::is_signed<T>::value
is true
if and only if T
is signed (BTW, it is also true
for floating-point types, if you don't need it, consider combining with std::is_integral
).
std::enable_if_t<Test, Type>
is the same as std::enable_if<Test, Type>::type
. std::enable_if<Test, Type>
is defined as an empty struct in case Test
is false and as a struct with an only typedef type
equal to template parameter Type
otherwise.
So, for signed types, std::enable_if_t<std::is_signed<T>::value, T>
is equal to T
, while for unsigned it's not defined and compiler uses SFINAE rule, so, if you need to specify an implementation for a particular non-signed type, you can easily do that:
template<>
unsigned diff(unsigned, unsigned)
{
return 0u;
}
Some relevant links: enable_if, is_signed.
Upvotes: 45
Reputation: 17483
As another option, you might probably add static_assert with std::is_signed type trait:
template<typename T>
auto diff(T x, T y)
{
static_assert(std::is_signed<T>::value, "Does not work for unsigned");
return x - y;
}
So that:
auto x = diff(4, 2); // works
auto x = diff(4U, 2U); // does not work
Upvotes: 7