user4938472
user4938472

Reputation: 145

What is the best way to express a templated numeric literal?

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

Issues under consideration

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).

Upvotes: 4

Views: 1076

Answers (5)

Steve Jessop
Steve Jessop

Reputation: 279265

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.

Upvotes: 3

user2249683
user2249683

Reputation:

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.

Upvotes: 3

Casey
Casey

Reputation: 42554

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 ;)

Upvotes: 0

Constructor
Constructor

Reputation: 7473

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.

Upvotes: 1

Potatoswatter
Potatoswatter

Reputation: 137830

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.)

Upvotes: 3

Related Questions