I have a function that could be reduced to something like this:
template<class T>
T foo(T x)
{
return 123.45 / x;
}
I would like to ensure that the numeric literal 123.45 is the same type as x. Let's say that T could be any numeric type: signed/unsigned char to long-long or float to long-double.
What is the most modern way to indicate that 123.45 should be of type T?
My requirements
- No warnings should be emitted (with every warning turned on).
- I want the decimal number 123.45 to have the precision of T when used in the calculation.
- I want the most elegant solution that achieves these goals.
Issues under consideration
- The “old-style cast”, i.e.
(T)123.45
is essentially deprecated in C++.
static_cast<T>(123.45)
seems like the right type of cast. However, it is verbose, and I have never seen this used on a literal.
- I am unsure if a suffix (e.g.
123.45L
) is necessary in order to get the maximum precision in the case of T being long double
, or if the compiler will automatically use the highest precision decimal-to-binary conversion.
Update 5-7-2014
After changing hundreds of casts in my codebase to the form T(123.45)
, I found that there are times when this syntax is unavailable. For example, you can not do long double(123.45)
because C++ does not support multitoken type constructors. In these cases, I have opted for static_cast<long double>(123.45)
.
What about a simple
template<class T>
T foo(T x)
{
return T(123.45) / x;
}
which will allow custom numeric classes to accept the value in a constructor.
static_cast
is the way to go. This is what the C cast is equivalent to, in any cast that preserves the original meaning but changes the type of the value.
If it's a class type with numeric features, static_cast
will call a one-argument constructor, even an explicit
one.
If you want to avoid narrowing conversions, you can use the syntax T{ 123.45 }
. This is not like a C-style cast, but uses direct-list-initialization. It still allows an explicit
constructor, but any numeric conversion to its parameter type must be exact. (No overflow or rounding allowed. For literals, this constraint is checked for the particular value, not as a relationship between types. However, GCC warns when I pass 12.0
to an int
; I'm not sure if that's right or not.)
The static cast is natural. If you haven't seen it, well, not everyone sees everything that happens :-) I tend to use C-style casts when (a) I know both types involved, (b) I'm entitled to be a bit lazy. As you'll see in the code below.
You do need the L
suffix if you want the precision when T
is more precise than double
. Potentially static_cast<long double>(123.45) != 123.45L
.
Suppose that T
was some even more precise type, like mpf_class
from GMP or some compiler-specific extended type beyond long double
. You might want to consider boost::lexical_cast<T>("123.45")
. Of course that won't work for integer types, you'd need two separate cases with enable_if
or whatever.
Also note that if T
is an integer type of lower rank than int
, then for example short(123.45) / x
is the same as int(123.45) / x
because the division is performed in int
either way and short(123.45) == int(123.45)
. And if T
is an integer type of higher rank than int
then for example long(123.45) / x
is still the same as int(123.45) / x
because the division will be performed in type T
either way and int(123.45) == long(123.45)
. This analysis relies on the fact that 123
has the same value in any integer type[*]. If you could get wraparound converting 123.45
to T
then things could be different. For example -1 / (unsigned char)2 != (unsigned char)-1 / (unsigned char)2
.
[*] I'm ignoring bool
, I can't actually remember whether it's a numeric type or not.
You may also try to do it in the following way:
// RealConstant.h
template <typename Real>
class RealConstant
{
public:
static
const Real value;
};
#define REAL_CONSTANT_DECLARATION(Real) \
template <> \
const Real RealConstant<Real>::value;
REAL_CONSTANT_DECLARATION(float)
REAL_CONSTANT_DECLARATION(double)
REAL_CONSTANT_DECLARATION(long double)
#undef REAL_CONSTANT_DECLARATION
// RealConstant.cpp
#include "RealConstant.h"
#define REAL_CONSTANT_DEFINITION(Real, constantValue) \
template <> \
const Real RealConstant<Real>::value = constantValue;
REAL_CONSTANT_DEFINITION(float, 123.45f)
REAL_CONSTANT_DEFINITION(double, 123.45)
REAL_CONSTANT_DEFINITION(long double, 123.45L)
#undef REAL_CONSTANT_DEFINTION
// File in which `foo` function template is defined
#include "RealConstant.h"
template<class T>
T foo(T x)
{
return RealConstant<T>::value / x;
}
Macros are not really needed, of course.
I would initialize a constant T
from the literal:
template<class T>
T foo(T x)
{
const T magic = 123.45;
return magic / x;
}
it's simultaneously more explicit and more readable than the static_cast
, and more importantly gives you the opportunity to document the magic number with a proper name ;)