Usage of noexcept with compile time expression

If we are given the following function:

1
2
3
4
5
6
7
template< typename T >
T sine( T const& a, T const& b ) noexcept
{
    static_assert( noexcept( T(a / sqrt(a * a  + b * b)) ), "throwing expr" );
    return a / sqrt(a * a  + b * b);
}


What exactly does this part of the code do?

T(a / sqrt(a * a + b * b)

It's not a function, so how can it be used in the noexcept operator to determine if it throws?
The first noexcept is the noexcept specifier;
the second noexcept is the noexcept operator.

1
2
3
4
5
6
7
8
9
10
#include <type_traits>
#include <cmath>

template < typename T >
auto sine( T a, T b,
           typename std::enable_if< std::is_arithmetic<T>::value >::type* = nullptr )
    noexcept( noexcept( a / std::sqrt( a*a + b*b ) ) )
{
    return a / std::sqrt( a*a  + b*b ) ;
}
I didn't ask about the noexcept specifier nor the noexcept operator. I know what both of those are. I asked about the T(...). What does that mean? Why is the expression surrounded by it? Does it call a function to see if it throws?

EDIT: I looked at your example. I'm not sure what this line you added is or what it does:

typename std::enable_if< std::is_arithmetic<T>::value >::type* = nullptr

Could you elaborate please?
Last edited on
The T(...) bit creates a temporary object of type T, as would happen as part of the return statement on line 5 of your function. EDIT: More accurately it's a cast, but a lot of what casting does is temporary object construction, so. *shrug*

The typename std::enable_if< std::is_arithmetic<T>::value >::type* = nullptr bit, despite being fairly arcane looking, does something that's pretty simple to describe: if T isn't an arithmetic type (i.e. integer or some floating point type), then it prevents this sine function template from being used (though overloads absolutely can still be).

The way it does this is thanks to SFINAE. To summarize (and simplify a bit): when compiling, thanks to how overloading works, the compiler needs to consider a list of possible functions to determine which one to use. If one of those is a function template, then the compiler splices in its template arguments, and if it winds up with ill-formed code (e.g. the code references members that don't actually exist), that shouldn't cause an error, but instead the function template should be quietly dropped from the list of possible functions. TL;DR: Substitution Failure Is Not An Error.

std::enable_if takes advantage of this. If the expression between its angle brackets evaluates to false, then its ::type member doesn't exist, which causes a substitution failure. Otherwise, it does, and the substitution failure doesn't happen. What's ::type? It's a type member that is void by default (you can change it by giving enable_if a second template argument). Meaning, if T is an arithmetic type, then the whole typename std::enable_if< std::is_arithmetic<T>::value >::type* = nullptr bit resolves to void* = nullptr, in which an unnamed parameter is given a default value of null.

Neat, no?

-Albatross
Last edited on
Topic archived. No new replies allowed.