Как использовать неявный вывод типа шаблона - PullRequest
0 голосов
/ 18 февраля 2019

Я пытаюсь написать шаблон для вычисления мощности числа во время компиляции (я не эксперт по метапрограммированию шаблонов, поэтому любые комментарии приветствуются).Ниже приведен код:

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

Я прочитал следующее, но до сих пор не смог найти ответ (кажется, что это совсем наоборот) это , это это .

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Конечно, вы можете пропустить тип decl, и вам не понадобятся структуры при использовании C ++ 11 contexpr.Например:

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

PS Более быстрый способ для гоночного числа в силе.Реальный код должен использовать что-то подобное, так как время компиляции также имеет значение.

#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;
}
0 голосов
/ 18 февраля 2019

Начиная с 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;
};

И вы также можете видеть, что при частичной специализации 01 частичная специализация излишня (также C ++ 11 / C ++ 14).

До C ++ 17 ... лучшее, что я могу себе представить, чтобы избежать явного указания типа T,пройти макроопределение (которое обычно сильно обескураживает, но в этом случае, я полагаю, может быть разумным).

Что-то вроде

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