【问题标题】:Passing an integer or a type as a template parameter?将整数或类型作为模板参数传递?
【发布时间】:2012-11-04 17:18:22
【问题描述】:

这是我正在尝试做的一个示例案例(这是一个“测试”案例,只是为了说明问题):

#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;
}

如您所见,f() 函数有两个版本:第一个采用int 作为模板参数,第二个采用std::ratio。问题如下:

我想通过g() 来“包装”这个函数,它可以将intstd::ratio 作为第一个模板参数并调用f() 的良好版本。

如何在不编写两个 g() 函数的情况下做到这一点?换句话说,我要写什么而不是/*An int OR a type*/

【问题讨论】:

    标签: c++ templates c++11 metaprogramming


    【解决方案1】:

    我会这样做,但我稍微改变了你的界面:

    #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
    

    注意事项:

    1. 我利用constexpr通过模板参数传递编译时常量(这就是constexpr 的用途)。

    2. g 现在只是一个完美的转发器。但是我无法使用std::forward,因为它没有用constexpr 标记(可以说是C++11 中的一个缺陷)。所以我放弃使用static_cast&lt;T&amp;&amp;&gt;。完美转发在这里有点矫枉过正。但是完全熟悉它是一个很好的成语。

    【讨论】:

    • 在您的编辑中:为什么不命名您的std::ratio 参数并使用r.numr.den? :)
    • 也许还可以解释为什么您专门使用成员而不是 ND,而不仅仅是在编辑历史记录中。
    【解决方案2】:

    如何在不编写两个 g() 函数的情况下做到这一点?

    你没有。在 C++ 中,除了通过重载之外,没有办法获取某个类型或某个类型的值。

    【讨论】:

      【解决方案3】:

      模板参数不可能同时采用类型和非类型值。

      解决方案 1:

      重载函数。

      解决方案 2:

      您可以将值存储在类型中。例如:

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

      但使用此解决方案,您必须指定 g&lt;store_int&lt;42&gt; &gt;(...) 而不是 g&lt;42&gt;(...)

      如果函数很小,我建议你使用重载。

      【讨论】:

      • 解决方案 2 可以只使用std::ratio
      • 我同意你的例子。但在某些情况下它可能有用(例如:不是std::ratio,而是另一个需要大量参数的模板)。
      猜你喜欢
      • 2019-04-24
      • 1970-01-01
      • 1970-01-01
      • 2013-11-08
      • 1970-01-01
      • 2015-07-29
      • 1970-01-01
      • 2016-10-20
      • 1970-01-01
      相关资源
      最近更新 更多