Passing an integer or type as a template parameter?

Here is an example of what I'm trying to do (this is a β€œtest” case to illustrate the problem):

#include <iostream> #include <type_traits> #include <ratio> template<int Int, typename Type> constexpr Type f(const Type x) { return Int*x; } template<class Ratio, typename Type, class = typename std::enable_if<Ratio::den != 0>::type> constexpr Type f(const Type x) { return (x*Ratio::num)/Ratio::den; } template</*An int OR a type*/ Something, typename Type> constexpr Type g(const Type x) { return f<Something, Type>(x); } int main() { std::cout<<f<1>(42.)<<std::endl; std::cout<<f<std::kilo>(42.)<<std::endl; } 

As you can see, there are two versions of the f() function: the first takes int as a template parameter, and the second takes the value std::ratio . The problem is this:

I would like to "wrap" this function with g() , which can take an int OR a std::ratio as the first parameter of the template and call a good version of f() .

How to do this without writing two g() functions? In other words, what do I need to write instead of /*An int OR a type*/ ?

+4
source share
3 answers

Here's how I would do it, but slightly changed the interface:

 #include <iostream> #include <type_traits> #include <ratio> template <typename Type> constexpr Type f(int Int, Type x) { return Int*x; } template <std::intmax_t N, std::intmax_t D, typename Type> constexpr Type f(std::ratio<N, D> r, Type x) { // Note use of r.num and r.den instead of N and D leads to // less probability of overflow. For example if N == 8 // and D == 12, then r.num == 2 and r.den == 3 because // ratio reduces the fraction to lowest terms. return x*r.num/r.den; } template <class T, class U> constexpr typename std::remove_reference<U>::type g(T&& t, U&& u) { return f(static_cast<T&&>(t), static_cast<U&&>(u)); } int main() { constexpr auto h = g(1, 42.); constexpr auto i = g(std::kilo(), 42.); std::cout<< h << std::endl; std::cout<< i << std::endl; } 42 42000 

Notes:

  • I used constexpr to not pass compile time constants through the template parameters (for which constexpr ).

  • g now just the perfect shipping agent. However, I was not able to use std::forward because it is not marked with constexpr (possibly a defect in C ++ 11). So I refused to use static_cast<T&&> instead. Perfect forwarding is a bit overdone here. But this is a good idiom that should be familiar.

+2
source

How to do this without writing two g () functions?

No. In C ++, there is no way to take either a type or a value of any type, except for overloading.

+1
source

It is not possible for a template parameter to accept both type and non-type values.

Solution 1:

Overloaded functions.

Solution 2:

You can store values ​​in types. Example:

 template<int n> struct store_int { static const int num = n; static const int den = 1; }; template<class Ratio, typename Type, class = typename std::enable_if<Ratio::den != 0>::type> constexpr Type f(const Type x) { return (x*Ratio::num)/Ratio::den; } template<typename Something, typename Type> constexpr Type g(const Type x) { return f<Something, Type>(x); } 

But with this solution you will need to specify g<store_int<42> >(...) instead of g<42>(...)

If the function is small, I recommend using overload.

0
source

Source: https://habr.com/ru/post/1443974/


All Articles