【问题标题】:How to use implicit template type deduction如何使用隐式模板类型推导
【发布时间】:2019-02-18 13:41:04
【问题描述】:

我正在尝试编写一个模板来在编译期间计算数字的幂(我不是模板元编程专家,因此感谢任何评论)。下面是代码:

template<typename T, T X, uint64_t P>
struct Pow
{
    static constexpr T result = X * Pow<T,X, P - 1>::result;
};
template<typename T, T X>
struct Pow<T, X, 0>
{
    static constexpr T result = 1;
};
template<typename T, T X>
struct Pow<T, X, 1>
{
    static constexpr T result = X;
};

我需要这样称呼:

Pow<decltype(4), 4, 2>::result

问题:有什么办法可以编写帮助模板,让调用跳过decltype?例如:

Pow<4, 2>::result

我已阅读以下内容,但到目前为止我还没有看到答案(似乎完全相反)thisthisthis

【问题讨论】:

  • 不是模板类,但在您的情况下,constexpr 函数是否同样有效?
  • 感谢您的回答。我也在看,但我发现很难强迫它以 constrexpr 方式使用。这就是代码的样子: template constexpr T Pow(T num, unsigned int pow) { return pow == 0 ? 1 : num * Pow(num, pow - 1); }
  • 所需要的只是您现有的模板类,如问题所示,以及一个返回Pow&lt;T, x, y&gt;::resultconstexpr 函数;在编译时,这将在逻辑上等同于您在此处拥有的内容。
  • 我有点害怕人们在 std::cout 中使用它,据我了解,这会绕过模板的 constrexpr-ness。 (我可能是错的,它可能是一样的,但我的理解是,这更依赖于上下文,因为它在哪里保持它的 constexpreness)
  • 也许指数应该是无符号类型,而不是有符号类型。它将在Pow&lt;double, 2.0, -1&gt; 中断。

标签: c++ templates template-meta-programming decltype


【解决方案1】:

从 C++17 开始,您可以将 auto 类型用于 X 模板值

template <auto X, int64_t P>
struct Pow
{
    static constexpr decltype(X) result = X * Pow<X, P - 1>::result;
};

template <auto X>
struct Pow<X, 0>
{
    static constexpr decltype(X) result = 1;
};

您还可以看到,鉴于 0 部分特化,1 部分特化是多余的(也是 C++11/C++14)。

在 C++17 之前...我能想象的最好的,为了避免显式 T 类型,通过宏定义(这通常是非常不鼓励的,但在这种情况下,我认为可能是合理的) .

某事

#define PowMacro(X, P)  Pow<decltype(X), X, P> 

【讨论】:

  • "1 部分特化是多余的"...除非T 是一个类类型,表示某种没有单位或半群的数学环,或者由于某种原因不支持从表达式1
  • @aschepler - 好点...从理论的角度来看...但是,实际上,T 类型,witch 的值可以是模板参数,不能接受初始化来自1 但支持乘法?
【解决方案2】:

当然可以跳过 decltype,并且在使用 C++ 11 上下文时不需要结构。例如:

#include <iostream>
#include <type_traits>

template<typename T, class = typename std::enable_if< std::is_arithmetic<T>::value >::type >
constexpr T pow(T n, T power) noexcept {
    return power == 1 ? n : n * pow(n,power - 1);
}

int main(int argc, const char* argv) {

    static_assert( 4 == pow(2,2) ,"wrong pow");
    static_assert( 8.0F == pow(2.0F,3.0F) ,"wrong pow");
    static_assert( 256.0 == pow(2.0,8.0) ,"wrong pow");

    std::cout << "integer 2^2=" << pow(2, 2) << std::endl;
    std::cout << "float 2^3=" << pow(2.0F, 3.0F) << std::endl;
    std::cout << "double 2^8=" << pow(2.0, 8.0) << std::endl;

    return 0;
}

附: 以更快的方式在力量中比赛数字。真正的代码应该使用类似的东西,因为编译时间也很重要。

#include <iostream>
#include <type_traits>

// https://en.wikipedia.org/wiki/Exponentiation_by_squaring
template<typename T>
constexpr T pow(const T base,const T power, typename std::enable_if< std::is_integral<T>::value >::type* = 0) {
    return  1 == power
            ? base
            : 0 == power
              ? 1
              : (1 == (power & 1) )
                ? base * pow(base, power - 1)
                : pow(base, (power >> 1) ) * pow( base, (power >> 1) );
}


#ifdef __GNUG__

  // GCC able to use most of <cmath> at compile time, check <cmath> header

  inline constexpr float pow(float base, float power) noexcept {
    return __builtin_powf(base, power);
  }

  inline constexpr double pow(double base, double power) noexcept {
    return __builtin_pow(base, power);
  }

  inline constexpr long double pow(long double base,long double power) noexcept {
    return __builtin_powl(base, power);
  }

#else

// slow
template<typename T>
constexpr T pow(T base, T power, typename std::enable_if< std::is_floating_point<T>::value >::type* = 0) noexcept {
    return power == 1.0 ? base : base * pow(base,power - static_cast<T>(1.0) );
}

#endif // __GNUG__


int main(int argc, const char** argv) {

    static_assert( 4 == pow(2,2) ,"wrong pow");
    static_assert( 1024 == pow(2L,10L) ,"wrong pow");
    static_assert( (1 << 20) == pow(2LL,20LL) ,"wrong pow");

    std::cout << "integer 2^1=" << pow(2, 1) << std::endl;
    std::cout << "integer 2^2=" << pow(2, 2) << std::endl;
    std::cout << "long 2^10=" << pow(2L, 10L) << std::endl;
    std::cout << "long long 2^20=" << pow(2LL, 20LL) << std::endl;

    static_assert( 8.0F == pow(2.0F,3.0F) ,"wrong pow");
    static_assert( 256.0 == pow(2.0,8.0) ,"wrong pow");
    static_assert( 1024.0L == pow(2.0L,10.0L) ,"wrong pow");

    std::cout << "float 2^3=" << pow(2.0F, 3.0F) << std::endl;
    std::cout << "double 2^8=" << pow(2.0, 8.0) << std::endl;
    std::cout << "long double 2^10=" << pow(2.0L, 10.0L) << std::endl;

    return 0;
}

【讨论】:

  • 感谢您的回答。我也读过这个,但我有一个问题。如果没有 static_assert 位,就无法保证计算出的编译时间。这是正确的吗?
  • constexpr 所以如果出现类似long double foo(long double bar, float buz) { return log(bar,MATH_PI) * pow(bar, sqrt(buz) ) ; } off 的情况,因为它不会是编译时间。但在这种情况下,我认为 - 它不应该是编译时间。不像static constexpr long double FOO = pow(128.0,500.0D);
  • 顺便说一句,最好的检查方法——编译器汇编输出。
  • 如果你想在pow参数不是constexpr时出现编译时错误,你可以static_assert(n == n, "not constexpr call"); static_assert(power == power, "not constexpr call");
  • 当您在编译时上下文中调用函数时(通过将结果存储为constexpr 或使用static_assert),您的编译器将自动无法使用某种“no constexpr “-信息。我在检查常量参数时看不到任何价值...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多