Reputation: 1141
This answer contains the following code:
#include <type_traits>
template<
typename T, //real type
typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type
> struct S{};
int main() {
S<int> s; //compiles
S<char*> s; //doesn't compile
}
The requirement that T
satisfy is_arithmetic
can easily be defeated, though, by specifying an arbitrary second type argument for the template S
, e.g. S<char*, void>
. Is there a way to foreclose this possibility?
Upvotes: 1
Views: 177
Reputation: 1
Is there a way to foreclose this possibility?
Yes, there is as shown below. In particular, we can make the second parameter a non-type parameter with a default.
template<
typename T,
//make the second parameter a non-type parameter with default
typename std::enable_if<std::is_arithmetic<T>::value, nullptr_t>::type N = nullptr
> struct S{};
int main() {
S<int> s; //compiles
//S<int, void> S2; //DOESN'T COMPILE
//S<int, 1> S3; //DOESN'T COMPILE
}
Upvotes: 3
Reputation: 3543
Use concept will be much easier (since you have mentioned),
template <typename T>
concept Arithmetic = std::is_arithmetic_v<T>;
template<Arithmetic T> struct S{};
Upvotes: 5
Reputation: 33864
The requirement that T satisfy is_arithmetic can easily be defeated
I mean, it is SFINAE, aka substitution is not an error. If you want to force specific types in templates, you can use concepts in c++20, or in c++17 - c++11, you have static_assert
:
template<
typename T //real type
> struct S{
static_assert(std::is_arithmetic_v<T>, "T must be arithmetic");
};
There is no defeating that.
Upvotes: 6