Reputation: 3474
I would like to get a compiler error in case I pass a float
number to my class that expect an int
in the code below. The code below runs with gcc 4.9.3
gcc -std=c++98 -O0 -g3 -Wall -Wextra -c -fmessage-length=0
How should I change the code that it will return compile error in case of different in types between the class instance type and the methods input argument type?
#include <iostream>
template <typename T>
class CheckValidity {
public:
CheckValidity(){};
T isFoo(T variable)
{
T result = static_cast<T>(0);
if ( variable > static_cast<T>(5) )
{
result = static_cast<T>(1);
}
return result;
};
};
int main() {
CheckValidity<int> checkValidityInt;
std::cout << checkValidityInt.isFoo(6.0f) << std::endl;
return 0;
}
Upvotes: 2
Views: 462
Reputation: 244682
The reason you are not getting a warning now is because the float
value 6.0f
can be losslessly converted into an int
. That is, you don't lose any precision during the conversion, because 6.0 can be exactly represented as 6. The compiler knows this because you are using compile-time constants.
If you changed it to 6.1f
, for example, you should get a warning (assuming you have -Wconversion
enabled):
int main() {
CheckValidity<int> checkValidityInt;
std::cout << checkValidityInt.isFoo(6.1f) << std::endl;
return 0;
}
In function 'int main()':
20 : warning: conversion to 'int' alters 'float' constant value [-Wfloat-conversion]
std::cout << checkValidityInt.isFoo(6.1f) << std::endl;
You would also get a warning if you were passing a variable of type float
, rather than a constant. This means the warning should catch any potential problems. A precise conversion of a constant is not a real problem.
To convert this warning into an error (making sure you do not miss it), also pass the -Werror
flag.
Upvotes: 2
Reputation: 206557
Make the member function a template member function. It opens up a few possibilities for dealing with different types.
Use static_assert
and std::is_floating_point
to make sure that the function is not called using floating point types.
template <typename U>
T isFoo(U variable)
{
static_assert(std::is_floating_point<U>::value == false,
"Can't handle floating point types");
T result = static_cast<T>(0);
if ( variable > static_cast<T>(5) )
{
result = static_cast<T>(1);
}
return result;
};
To make sure that U
and T
are the same type, you can use std::is_same
:
template <typename U>
T isFoo(U variable)
{
static_assert(std::is_same<T, U>::value,
"Can't handle different types");
T result = static_cast<T>(0);
if ( variable > static_cast<T>(5) )
{
result = static_cast<T>(1);
}
return result;
};
To be able to deal with U
being a derived type of T
, you can use:
template <typename U>
T isFoo(U variable)
{
static_assert(std::is_base_of<T, U>::value,
"Can't handle incompatible types");
T result = static_cast<T>(0);
if ( variable > static_cast<T>(5) )
{
result = static_cast<T>(1);
}
return result;
};
Upvotes: 0
Reputation: 636
It sounds like you want to use std::is_integral
in combination with static_assert
.
In whatever function or class template you want to restrict to integers, you can call:
static_assert(std::is_integral<T>::value, "Integer required.");
You might also be interested in std::enable_if
(or the convenience typedef enable_if_t
):
template <typename Integer,
typename = std::enable_if_t<std::is_integral<Integer>::value>>
You can use call static_assert
with any integer constant expression to generate a compile-time error if the expression is false. You can use enable_if
to restrict the types with which a template can be instantiated (also causing a compiler error if you use an invalid type).
Edit: I just noticed that you're using -std=c++98. If you need to use that standard, I can delete this answer. But if you can use C++11, this will be better than what you can do in earlier standards.
Upvotes: 0
Reputation: 48918
You can use template specialization:
//Returns false in the default case
template<typename V>
bool isFoo(V)
{
return false;
};
//Returns true if the type passed is T
template<>
bool isFoo<T>(T)
{
return true;
}
The variable is still passed as parameter without a name, because you don't need it internally. It is also still used because isFoo<decltype(var)>()
is better than isFoo(var)
.
std::is_same
or similar constructs:
//You need a separate type, because if you use T, you will just cast the result to T
template<typename V>
bool isFoo(V)
{
//Compare the types
return std::is_same<V, T>::value;
};
Upvotes: 0
Reputation: 814
This effect is better achieved if you just leave common template class just declared, but not defined and specialize template class definition for float. I would try to play around something like type lists...
Upvotes: 0
Reputation: 5348
For c++98:
One way to achieve what you want of requiring an exact match is to have a template method which accepts any type but always fails due to SFINAE:
template<typename U>
U isFoo(U variable)
{
typename CheckValidity::InvalidType x;
}
For c++11:
You can use a deleted method to achieve the same effect more cleanly:
template<typename U>
U isFoo(U variable) = delete;
Upvotes: 2