4

Is there a way to limit a template parameter T to a specific type or category?

The below code works but I want to make it simpler:

#include <iostream>
#include <type_traits>


template <typename T>
constexpr auto func( const T num ) -> T
{
    static_assert( std::is_floating_point_v<T>, "Floating point required." );

    return num * 123;
}

int main( )
{
    std::cout << func( 4345.9 ) << ' ' // should be ok
              << func( 3 ) << ' ' // should not compile
              << func( 55.0f ) << '\n'; // should be ok
}

I want to get rid of the static_assert and write something like this:

template < std::is_floating_point_v<T> >
constexpr auto func( const T num ) -> T
{
    return num * 123;
}

Any suggestions? Anything from type_traits or maybe concepts would be better.

1
  • @Aconcagua: then just overload for float and double :) Commented Jan 19, 2022 at 16:03

2 Answers 2

10

You can use std::floating_point concept to constrain the type T:

#include <concepts>

template<std::floating_point T>
constexpr auto func( const T num ) -> T {
  return num * 123;
}

Demo

Sign up to request clarification or add additional context in comments.

Comments

4

The below code works but I want to make it simpler:

You may combine abbreviated function templates and the std::floating_point concept for a condensed constrained function template definition:

constexpr auto func(std::floating_point auto num) {
  return num * 123;
}

Note that this does not include an explicitly specified trailing return type T as in your original approach, but that for the current definition the deduced return type will be decltype(num), which is either float or double (or impl-defined long double). As @Barry points out in the comments below, if you require a trailing return type, say for an overload with a ref- and cv-qualified function parameter, then the brevity gain of abbreviated templates is lost to the added cost of complex trailing return type.

// Contrived example: but if this was the design intent,
// then no: skip the abbreviated function template approach.
constexpr auto func(const std::floating_point auto& num) 
  -> std::remove_cvref_t<decltype(num)> { /* ... */ }

// ... and prefer
template<std::floating_point T>
constexpr auto func(const T& num) -> T  { /* ... */ }

// ... or (preferential)
template<std::floating_point T>
constexpr T func(const T& num) { /* ... */ }

8 Comments

Note that OP's code explicitly returned -> T, which you can't do with abbreviated function templates.
@Barry: Sure you can, with decltype. (I seem to recall playing code golf on this subject before.)
@Barry I guess you could use constexpr auto func(std::floating_point auto num) -> decltype(num), but it would not always be equivalent to -> T unless also taking into account e.g. removing cv-qualifiers.
@digito_evo Our two solutions are functionally equivalent and there is no different whatsoever with regard to performance/generated assembly. Abbreviated function templates is just a syntactic sugar for regular function templates (with invented template parameters as compared to the explicitly declared type-template parameters in the template-head's of non-abbreviated function templates).
@Davis Once you need decltype, you might as well just not use abbreviated function templates. It both becomes longer and less readable.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.