如何去除模板参数的冗余并增加模板参数的灵活性?
How to remove redundancy from and add flexibility to template parameters?
我想使用 constexpr
,编译时生成的 std::arrays
用于快速值查找而不是冗长的运行时计算。为此,我起草了一个将在编译时执行的模板化 constexpr
函数。
请参阅以下示例代码,它允许超快地访问三角和斐波那契数和阶乘。
#include <iostream>
#include <utility>
#include <array>
constexpr size_t ArraySize = 20u;
// Some generator functions -------------------------------------------------------------
constexpr size_t getTriangleNumber(size_t row) noexcept {
size_t sum{};
for (size_t i{ 1u }; i <= row; i++) sum += i;
return sum;
}
constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
unsigned long long f1{ 0ull }, f2{ 1ull }, f3{};
while (index--) { f3 = f2 + f1; f1 = f2; f2 = f3; }
return f2;
}
constexpr unsigned long long getFactorial(size_t index) noexcept {
unsigned long long result{ 1 };
while (index > 0) { result *= index; --index; }
return result;
}
// Generate a std::array with n elements of a given type and a generator function -------
template <typename DataType, DataType(*generator)(size_t), size_t... ManyIndices>
constexpr auto generateArray(std::integer_sequence<size_t, ManyIndices...>) noexcept {
return std::array<DataType, sizeof...(ManyIndices)>{ { generator(ManyIndices)... } };
}
// The arrays ---------------------------------------------------------------------------
constexpr auto TriangleNumber = generateArray<size_t, getTriangleNumber>(std::make_integer_sequence<size_t, ArraySize>());
constexpr auto FibonacciNumber = generateArray<unsigned long long, getFibonacciNumber>(std::make_integer_sequence<size_t, ArraySize>());
constexpr auto Factorial = generateArray<unsigned long long, getFactorial>(std::make_integer_sequence<size_t, ArraySize>());
// Some debug test driver code
int main() {
for (const auto t : TriangleNumber) std::cout << t << ' '; std::cout << '\n';
for (const auto f : FibonacciNumber) std::cout << f << ' '; std::cout << '\n';
for (const auto f : Factorial) std::cout << f << ' '; std::cout << '\n';
return 0;
}
如你所见。该模板使用参数“DataType”。在我看来这是多余的。这始终是生成器函数的 return 类型。它还将确定 std::array
的数据类型
那么,我们怎样才能消除这种冗余,只使用生成器函数给定的类型呢?
另外。函数参数始终为 size_t
。还有一个冗余,它也不是很灵活。 “ManyIndices”的类型和函数参数始终相同。所以,不需要写那个 double.
关于灵活性。如果我想使用具有不同参数数据类型的生成器函数,例如 unsigned long long
如
constexpr unsigned long long factorial(unsigned long long n) noexcept {
return n == 0ull ? 1ull : n * factorial(n - 1ull);
}
我做不到。所以,基本上一切都应该从生成器函数签名中推导出来。
这也适用于像
这样的行
constexpr auto Factorial = generateArray<unsigned long long, getFactorial>(std::make_integer_sequence<size_t, ArraySize>());
这里,size_t
也是给定函数的参数类型
那么,如何消除冗余并增加灵活性呢?
DataType
可以从传递的生成器推导出来,使用 std::declval
.
std::integer_sequence
可以换成std::index_sequence
.
必须明确提供计算大小。
template <typename GEN, size_t ... Indices>
constexpr auto generateArray2Helper(GEN gen, std::index_sequence<Indices...>) {
return std::array<decltype(std::declval<GEN>()(size_t{})), sizeof...(Indices)>{ gen(Indices)... };
}
template <size_t N, typename GEN>
constexpr auto generateArray2(GEN gen) {
return generateArray2Helper(gen, std::make_index_sequence<N>());
}
// The arrays ---------------------------------------------------------------------------
constexpr auto TriangleNumber = generateArray2<ArraySize>(getTriangleNumber);
constexpr auto FibonacciNumber = generateArray2<ArraySize>(getFibonacciNumber);
constexpr auto Factorial = generateArray2<ArraySize>(getFactorial);
c++20版本:
template<std::size_t...Is>
constexpr auto index_over(auto f, std::index_sequence<Is...>){
return f(std::integral_constant<std::size_t,Is>{}...);
}
template<auto N>
constexpr auto index_upto(auto f){
return index_over(f, std::make_index_sequence<N>{});
}
template<auto size>
constexpr auto gen_array(auto f){
return index_upto<size>([&](auto...Is){
return std::array{f(Is)...};
});
}
我想使用 constexpr
,编译时生成的 std::arrays
用于快速值查找而不是冗长的运行时计算。为此,我起草了一个将在编译时执行的模板化 constexpr
函数。
请参阅以下示例代码,它允许超快地访问三角和斐波那契数和阶乘。
#include <iostream>
#include <utility>
#include <array>
constexpr size_t ArraySize = 20u;
// Some generator functions -------------------------------------------------------------
constexpr size_t getTriangleNumber(size_t row) noexcept {
size_t sum{};
for (size_t i{ 1u }; i <= row; i++) sum += i;
return sum;
}
constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
unsigned long long f1{ 0ull }, f2{ 1ull }, f3{};
while (index--) { f3 = f2 + f1; f1 = f2; f2 = f3; }
return f2;
}
constexpr unsigned long long getFactorial(size_t index) noexcept {
unsigned long long result{ 1 };
while (index > 0) { result *= index; --index; }
return result;
}
// Generate a std::array with n elements of a given type and a generator function -------
template <typename DataType, DataType(*generator)(size_t), size_t... ManyIndices>
constexpr auto generateArray(std::integer_sequence<size_t, ManyIndices...>) noexcept {
return std::array<DataType, sizeof...(ManyIndices)>{ { generator(ManyIndices)... } };
}
// The arrays ---------------------------------------------------------------------------
constexpr auto TriangleNumber = generateArray<size_t, getTriangleNumber>(std::make_integer_sequence<size_t, ArraySize>());
constexpr auto FibonacciNumber = generateArray<unsigned long long, getFibonacciNumber>(std::make_integer_sequence<size_t, ArraySize>());
constexpr auto Factorial = generateArray<unsigned long long, getFactorial>(std::make_integer_sequence<size_t, ArraySize>());
// Some debug test driver code
int main() {
for (const auto t : TriangleNumber) std::cout << t << ' '; std::cout << '\n';
for (const auto f : FibonacciNumber) std::cout << f << ' '; std::cout << '\n';
for (const auto f : Factorial) std::cout << f << ' '; std::cout << '\n';
return 0;
}
如你所见。该模板使用参数“DataType”。在我看来这是多余的。这始终是生成器函数的 return 类型。它还将确定 std::array
那么,我们怎样才能消除这种冗余,只使用生成器函数给定的类型呢?
另外。函数参数始终为 size_t
。还有一个冗余,它也不是很灵活。 “ManyIndices”的类型和函数参数始终相同。所以,不需要写那个 double.
关于灵活性。如果我想使用具有不同参数数据类型的生成器函数,例如 unsigned long long
如
constexpr unsigned long long factorial(unsigned long long n) noexcept {
return n == 0ull ? 1ull : n * factorial(n - 1ull);
}
我做不到。所以,基本上一切都应该从生成器函数签名中推导出来。
这也适用于像
这样的行constexpr auto Factorial = generateArray<unsigned long long, getFactorial>(std::make_integer_sequence<size_t, ArraySize>());
这里,size_t
也是给定函数的参数类型
那么,如何消除冗余并增加灵活性呢?
DataType
可以从传递的生成器推导出来,使用 std::declval
.
std::integer_sequence
可以换成std::index_sequence
.
必须明确提供计算大小。
template <typename GEN, size_t ... Indices>
constexpr auto generateArray2Helper(GEN gen, std::index_sequence<Indices...>) {
return std::array<decltype(std::declval<GEN>()(size_t{})), sizeof...(Indices)>{ gen(Indices)... };
}
template <size_t N, typename GEN>
constexpr auto generateArray2(GEN gen) {
return generateArray2Helper(gen, std::make_index_sequence<N>());
}
// The arrays ---------------------------------------------------------------------------
constexpr auto TriangleNumber = generateArray2<ArraySize>(getTriangleNumber);
constexpr auto FibonacciNumber = generateArray2<ArraySize>(getFibonacciNumber);
constexpr auto Factorial = generateArray2<ArraySize>(getFactorial);
c++20版本:
template<std::size_t...Is>
constexpr auto index_over(auto f, std::index_sequence<Is...>){
return f(std::integral_constant<std::size_t,Is>{}...);
}
template<auto N>
constexpr auto index_upto(auto f){
return index_over(f, std::make_index_sequence<N>{});
}
template<auto size>
constexpr auto gen_array(auto f){
return index_upto<size>([&](auto...Is){
return std::array{f(Is)...};
});
}