...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
When using the constants inside a function template, we need to ensure that
we use a constant of the correct precision for our template parameters. We
can do this by calling the function-template versions, pi<FPType>()
, of the constants like this:
#include <boost/math/constants/constants.hpp> template <class Real> Real area(Real r) { using namespace boost::math::constants; return pi<Real>() * r * r; }
Although this syntax is a little less "cute" than the non-template
version, the code is no less efficient (at least for the built-in types
float
, double
and long double
)
: the function template versions of the constants are simple inline functions
that return a constant of the correct precision for the type used. In addition,
these functions are declared constexp
for those compilers that support this, allowing the result to be used in
constant-expressions provided the template argument is a literal type.
Tip | |
---|---|
Keep in mind the difference between the variable version, just |
Note | |
---|---|
You can always use both variable and template-function versions provided calls are fully qualified, for example: double my_pi1 = boost::math::constants::pi<double>(); double my_pi2 = boost::math::double_constants::pi; |
Warning | |
---|---|
It may be tempting to simply define using namespace boost::math::double_constants; using namespace boost::math::constants; but if you do define two namespaces, this will, of course, create ambiguity! double my_pi = pi(); // error C2872: 'pi' : ambiguous symbol double my_pi2 = pi; // Context does not allow for disambiguation of overloaded function Although the mistake above is fairly obvious, it is also not too difficult to do this accidentally, or worse, create it in someone elses code. Therefore is it prudent to avoid this risk by localising the scope of such definitions, as shown above. |
Tip | |
---|---|
Be very careful with the type provided as parameter. For example, providing an integer instead of a floating-point type can be disastrous (a C++ feature). cout << "Area = " << area(2) << endl; // Area = 12!!! You should get a compiler warning warning : 'return' : conversion from 'double' to 'int', possible loss of data Failure to heed this warning can lead to very wrong answers!
You can also avoid this by being explicit about the type of cout << "Area = " << area<double>(2) << endl; // Area = 12.566371 |