Failing to accumulate compile time constants - c++

I am trying to create a variadic int argument vectors and perform + operators on them.
For example:
vec<1,2,3> v1;
vec<4,5> v2;
auto res = v1+v2;
res.print(); // should print 5 7 3 vec<5,7,3> is the result.
And now I want to create struct vecSum, which will sum up the vectors given to it;
namespace ex {
template<int... N>
struct vec
{
static const int size = sizeof...(N);
void print() {
((std::cout << N << " "), ...);
}
};
template<int...N, int...M, int ...S>
auto add(vec<N...>, vec<M...>, std::index_sequence<S...>) {
constexpr int arr1[sizeof...(S)]{ N... };
constexpr int arr2[sizeof...(S)]{ M... };
return vec<(arr1[S] + arr2[S])...>{};
}
template<int...N, int...M>
auto operator+(vec<N...> v1, vec<M...> v2) {
constexpr size_t size = std::max(sizeof...(N), sizeof...(M));
return add(v1, v2, std::make_index_sequence<size>{});
}
template<typename... Args>
auto all(Args... args) { return (... + args); }
template<typename... Ts>
struct vecSum
{
static constexpr auto res = all(Ts);
};
}
This test works fine:
ex::vec<1, 2, 3> v1;
ex::vec<4, 5> v2;
ex::vec<2> v3;
auto r = ex::all(v1,v2,v3);
r.print();
This prints 7 7 3;
What I want to achieve is:
vecSum<vec<1,3,4>, vec<3>, vec<3,4,5>> z;
z::res.print(); and I expect 7 7 9
Instead I have this error:
error C2275: 'Ts': illegal use of this type as an expression
error C2119: 'res': the type for 'auto' cannot be deduced from an empty initializer
Can someone please hint what's wrong here? I know I am trying to pass types parameter pack Ts as expression, is there a workaround or fix for this?

I see some little problems in your code...
expand Ts
In vecSum you define res as follows
static constexpr auto res = all(Ts);
You have to expand Ts as follows
// ...............................VVVVV
static constexpr auto res = all(Ts{}...);
std::index_sequence contains std::size_t, not int, values
Your add() is defined as follows
template<int...N, int...M, int ...S>
auto add(vec<N...>, vec<M...>, std::index_sequence<S...>)
But the S... values are std::size_t
// ........................VVVVVVVVVVV
template<int...N, int...M, std::size_t ...S>
auto add(vec<N...>, vec<M...>, std::index_sequence<S...>)
or template deduction doesn't works.
constexpr functions
If vecSum has to be constexpr, you have to define all necessary functions (add(), all(), operator+()) as constexpr.
print() const
The method vec::print() is better const, given that you want print a constexpr vecSum::res value.
The following is a full compiling corrected example
#include <utility>
#include <iostream>
#include <type_traits>
namespace ex {
template<int... N>
struct vec
{
static const int size = sizeof...(N);
void print() const {
((std::cout << N << " "), ...);
}
};
template<int...N, int...M, std::size_t ...S>
constexpr auto add(vec<N...>, vec<M...>, std::index_sequence<S...>) {
constexpr int arr1[sizeof...(S)]{ N... };
constexpr int arr2[sizeof...(S)]{ M... };
return vec<(arr1[S] + arr2[S])...>{};
}
template<int...N, int...M>
constexpr auto operator+(vec<N...> v1, vec<M...> v2) {
constexpr size_t size = std::max(sizeof...(N), sizeof...(M));
return add(v1, v2, std::make_index_sequence<size>{});
}
template<typename... Args>
constexpr auto all(Args... args) { return (... + args); }
template<typename... Ts>
struct vecSum
{
static constexpr auto res = all(Ts{}...);
};
}
int main ()
{
ex::vecSum<ex::vec<1,3,4>, ex::vec<3>, ex::vec<3,4,5>> z;
z.res.print();
}

Related

How to match callable objects out of parameter pack to corresponding element in an array as argument?

For example, say I have the following:
template<typename ...FunctionTypes>
static void MainFunction(FunctionTypes... functions)
{
constexpr Uint32_t NumFunctions= sizeof...(FunctionTypes);
std::array<double, NumFunctions> myArray;
double arg1 = 4.2;
int arg2= 9;
for_each_tuple(myArray, FigureOutThisPart(myFunctions, arg1, arg2)...);
}
Where for_each_tuple takes a tuple or array of size N as well as N functions to apply to each tuple entry. This part was tricky to implement, but works! It is defined as such:
namespace detail {
template <typename Tuple, std::size_t ...Indices, typename ...FunctionTypes>
constexpr void for_each_tuple_impl(Tuple&& tuple, std::index_sequence<Indices...>, FunctionTypes&&... functionsIn) {
std::tuple<FunctionTypes...> functions = std::tie(functionsIn...);
using swallow = int[];
(void)swallow{
1, // Make sure array has at least one element
(std::get<Indices>(functions)(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
};
}
}
template <typename Tuple, typename ...Functions>
void for_each_tuple(Tuple&& tuple, Functions&&... f) {
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
static_assert(N == sizeof...(Functions), "Need one function per tuple entry");
detail::for_each_tuple_impl(
std::forward<Tuple>(tuple),
std::make_index_sequence<N>{},
std::forward<Functions>(f)...);
}
The idea is that I have set of myFunctions, each of which is operating on a different entry of myArray. Each function in myFunctions would take arg1, arg2, and the current array entry as arguments.
My goal is to be able to pass arg1 and arg2 into each of myFunctions so that those values can be used in the operation on the current array entry. Is there a sane way in which this can be done? Ideally, I would like the solution to be a constexpr so everything can get resolved at compile time.
I think something like this could work:
#include <array>
#include <functional>
template<typename ...FunctionTypes>
constexpr void MainFunction(FunctionTypes... functions)
{
constexpr auto NumFunctions= sizeof...(FunctionTypes);
std::array<double, NumFunctions> myArray{};//Zero-init for now.
double arg1 = 4.2;
int arg2= 9;
std::size_t i=0;
(std::invoke(functions, arg1,arg2, myArray[i++]),...);
}
#include <iostream>
void foo(double a1, int a2, int a){
std::cout<<"Called foo("<<a1<<','<<a2<<','<<a<<")\n";
}
int main()
{
MainFunction(foo,foo);
return 0;
}
It requires C++17 for the fold expression and the sequence points for the comma.
It can be evaluated at compile-time:
constexpr void bar(double a1, int a2, int a){
}
int main()
{
constexpr auto x = (MainFunction(bar,bar),1); // Force compile-time evaluation.
return 0;
}
I would just make a tuple out of functors at very beginning and match an element of the tuple to a corresponding element of the array:
template<std::size_t I, typename E, typename ...F, typename ...Args>
constexpr void apply(const std::array<E, sizeof...(F)>& elements,
const std::tuple<F...>& functions,
Args... args) {
std::get<I>(functions)(std::get<I>(elements), args...);
}
template<std::size_t ...I, typename E, typename ...F, typename ...Args>
constexpr void for_each(std::index_sequence<I...>,
const std::array<E, sizeof...(I)>& elements,
const std::tuple<F...>& functions,
Args... args) {
(apply<I>(elements, functions, args...),...);
}
template<typename ...F>
constexpr static void MainFunction(F... functors)
{
constexpr std::size_t size = sizeof...(F);
constexpr std::array<double, size> elements{};
constexpr double arg1 = 4.2;
constexpr int arg2 = 9;
for_each(std::make_index_sequence<size>(),
elements,
std::forward_as_tuple(functors...),
arg1, arg2);
}
The client code will look something like that:
void function(double val, double arg1, int arg2) {
std::cout << arg1 + arg2 + 8 * val << std::endl;
}
int main() {
MainFunction(function, [](double val, double arg1, double arg2) {
std::cout << val / arg1 + arg2 << std::endl;
});
return 0;
}
As long as argument functions are constexpr, the implementation is also constexpr:
template<typename ...F>
constexpr static auto MainFunction(F... functors) { ... return elements; }
constexpr void function(double val, double arg1, int arg2) { ... }
int main() {
// Retrieves the constexpr elements array from the function
constexpr auto val = MainFunction(function, [](double, double, int){ ... });
}
You can use std::bind_front to construct a function that will have placeholder arguments, which is a solution a little closer to what you originally intended. This, however, has a drawback of not being runnable in constexpr context:
template <std::size_t N, typename T, typename... Functions>
auto for_each_tuple(std::array<T, N> const& array, Functions... functions) {
auto index = 0uz;
(std::invoke(functions, array[index++]), ...);
}
template<typename ...FunctionTypes>
static void MainFunction(FunctionTypes... functions)
{
constexpr std::uint32_t NumFunctions= sizeof...(FunctionTypes);
std::array<double, NumFunctions> myArray{};
double arg1 = 4.2;
int arg2= 9;
for_each_tuple(myArray, std::bind_front(functions, arg1, arg2)...);
}
auto main() -> int {
auto print_thrice = [](double d1, int i2, double arr) {
std::cout << d1 << ' ' << i2 << ' ' << arr << '\n';
};
MainFunction(print_thrice, print_thrice, print_thrice);
}

How to choose type from some different on compilation time?

I want to do something like this:
template <uint64_t N>
struct a {
static constexpr T1 v1 = {};
static constexpr T2 v2 = {};
static constexpr auto v3 = (N % 2 == 1 ? v1 : v2);
};
But I can't use (? :) with different types. How I can do this?
For example, you can use if constexpr and a lambda function (C++17):
template <std::uint64_t N>
struct a {
static constexpr T1 v1 = {};
static constexpr T2 v2 = {};
static constexpr auto v3 =
[] {
if constexpr (N % 2 == 1)
return v1;
else
return v2;
}();
};
Another solution with a std::tuple (C++14):
template <std::uint64_t N>
struct a {
static constexpr T1 v1 = {};
static constexpr T2 v2 = {};
static constexpr auto v3 = std::get<N % 2>(std::make_tuple(v2, v1));
};
If you can use C++17 (but you tagged C++11 and C++14 only) a if constexpr solution based (very elegant the lambda based from Evg) is preferable, IMHO.
Before C++17, I suppose you can try using SFINAE. By example
#include <type_traits>
template <int N>
struct Foo
{
static constexpr int v1 = {};
static constexpr long v2 = {};
template <int M = N>
constexpr static std::enable_if_t<M % 2 == 1, int> getV3 ()
{ return v1; }
template <int M = N>
constexpr static std::enable_if_t<M % 2 != 1, long> getV3 ()
{ return v2; }
static constexpr auto v3 = getV3();
};
int main ()
{
static_assert( std::is_same_v<int const, decltype(Foo<1>::v3)> );
static_assert( std::is_same_v<long const, decltype(Foo<2>::v3)> );
}
As suggested by Evg (thanks!) you can avoid SFINAE using overloading (good-old tag dispatching). By example
template <int N>
struct Foo
{
static constexpr int v1 = {};
static constexpr long v2 = {};
constexpr static auto getV3 (std::true_type)
{ return v1; }
constexpr static auto getV3 (std::false_type)
{ return v2; }
static constexpr auto v3 = getV3(std::integral_constant<bool, N%2>{});
};

C++ "forgetting" that variable is constexpr when used as function argument

I have the following code where I am irritated by the fact that compiler is unable to see that variable passed as argument to a function is constexpr so I must use arity 0 function instead of 1 argument function.
I know this is not a compiler bug, but I wonder if there are idioms that enable to workaround this problem.
#include <array>
#include <iostream>
static constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
template <typename C, typename P, typename Y>
static constexpr void copy_if(const C& rng, P p, Y yi3ld) {
for (const auto& elem: rng) {
if (p(elem)){
yi3ld(elem);
}
}
}
// template<std::size_t N>
static constexpr auto get_evens(/* const std::array<int, N>& arr */) {
constexpr auto is_even = [](const int i) constexpr {return i % 2 == 0;};
constexpr int cnt = [/* &arr, */&is_even]() constexpr {
int cnt = 0;
auto increment = [&cnt] (const auto&){cnt++;};
copy_if(arr, is_even, increment);
return cnt;
}();
std::array<int, cnt> result{};
int idx = 0;
copy_if(arr, is_even, [&result, &idx](const auto& val){ result[idx++] = val;});
return result;
}
int main() {
// constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
for (const int i:get_evens(/* arr */)) {
std::cout << i << " " << std::endl;
}
}
If it is not obvious what I want: I would like to change get_evens signature so that it is template templated on array size N and that it takes 1 argument of type const std::array<int, N>&.
The error message when I change arr to be an function argument isn't helpful:
prog.cc:25:21: note: initializer of 'cnt' is not a constant expression
prog.cc:19:19: note: declared here
constexpr int cnt = [&arr, &is_even]()constexpr {
A function argument is never a constant expression, even if a function is used in constexpr context:
constexpr int foo(int i)
{
// i is not a constexpr
return i + 1;
}
constexpr auto i = 1;
constexpr auto j = foo(i);
To mimic a constexpr argument, use a template parameter:
template<int i>
constexpr int foo()
{
// i is constexpr
return i + 1;
}
constexpr auto i = 1;
constexpr auto j = foo<i>();
A possible solution is to use std::integer_sequence to encode integers into a type:
#include <array>
#include <iostream>
#include <type_traits>
template<typename P, typename Y, int... elements>
constexpr void copy_if_impl(P p, Y yi3ld, std::integer_sequence<int, elements...>) {
((p(elements) && (yi3ld(elements), true)), ...);
}
template<typename arr_t, typename P, typename Y>
constexpr void copy_if(P p, Y yi3ld) {
copy_if_impl(p, yi3ld, arr_t{});
}
template<typename arr_t>
constexpr auto get_evens(){
constexpr auto is_even = [](const int i) constexpr { return i % 2 == 0; };
constexpr int cnt = [&is_even]() constexpr {
int cnt = 0;
auto increment = [&cnt](const auto&) { cnt++; };
copy_if<arr_t>(is_even, increment);
return cnt;
}();
std::array<int, cnt> result{};
int idx = 0;
copy_if<arr_t>(is_even, [&result, &idx](const auto& val) {
result[idx++] = val; });
return result;
}
int main()
{
using arr = std::integer_sequence<int, 11, 22, 33, 44, 55>;
for (const int i : get_evens<arr>()) {
std::cout << i << " " << std::endl;
}
}
Addition suggested by Constantinos Glynos.
From Effective Modern C++ book by Scott Meyers, item 15, p.98:
constexpr functions can be used in contexts that demand compile-time constants. If the values of the arguments you pass to a constexpr function in such a context are known during compilation, the result will be computed during compilation. If any of the arguments’ values is not known during compilation, your code will be rejected.
When a constexpr function is called with one or more values that are not known during compilation, it acts like a normal function, computing its result at runtime. This means you don’t need two functions to perform the same operation, one for compile-time constants and one for all other values. The constexpr function does it all.
The other answer has a correct work around but I think the reasoning has nothing to do with parameters but instead to do with the lambda capture here:
constexpr int cnt = [/* &arr, */&is_even]()
Indeed we can test the various scenarios with this code:
#include <array>
#include <iostream>
template <size_t N>
constexpr int foo(const std::array<int, N>& arr) {
return [&arr] () { return arr.size(); }();
}
template <size_t N>
constexpr int bar(const std::array<int, N>& arr) {
int res{};
for (auto i : arr) {
res++;
}
return res;
}
template <size_t N>
constexpr int baz(const std::array<int, N>& arr) {
constexpr int test = [&arr] () constexpr {
return bar(arr);
}();
return test;
}
int main() {
constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
constexpr std::array<int, foo(arr)> test{};
constexpr std::array<int, bar(arr)> test2{};
constexpr std::array<int, baz(arr)> test3{};
}
Note that the line where test3 is initialized fails to compile. This, however, compiles just fine:
template <size_t N>
constexpr int baz(const std::array<int, N>& arr) {
return bar(arr);
}
So, what's the problem here? Well lambdas are really just glorified functors, and internally it'll look something like this:
struct constexpr_functor {
const std::array<int, 5>& arr;
constexpr constexpr_functor(const std::array<int, 5>& test)
: arr(test) { }
constexpr int operator()() const {
return bar(arr);
}
};
// ...
constexpr constexpr_functor t{arr};
constexpr std::array<int, t()> test3{};
Notice now that we get an error message showing the real problem:
test.cpp:36:33: note: reference to 'arr' is not a constant expression
test.cpp:33:34: note: declared here
constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
The other answer quotes Scotts Meyer's book but misinterprets the quotes. The book actually shows several examples of parameters being used in constexpr situations, but the quotes are simply saying that if you pass a non-constexpr parameter, the function can run at compile-time.
Following the Evg's suggestion, so passing the numbers as template parameters of a std::integer_sequence, but passing the integer sequence as argument of the get_evens() function, and not as template parameter, you can use the numbers directly inside get_evens().
I mean... you can simplify the get_evens() as follows (EDIT: further simplified following a suggestion from Evg (Thanks!))
template <typename T, T ... Ts>
constexpr auto get_evens (std::integer_sequence<T, Ts...> const &)
{
std::array<T, (std::size_t(!(Ts & T{1})) + ...)> result{};
std::size_t idx = 0;
((void)(Ts & 1 || (result[idx++] = Ts, true)), ...);
return result;
}
and you can use it this way
int main()
{
using arr = std::integer_sequence<int, 11, 22, 33, 44, 55>;
for ( const int i : get_evens(arr{}) )
std::cout << i << " " << std::endl;
}
#include <array>
#include <iostream>
static constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
template <typename C, typename P, typename T>
static constexpr void invoke_if(const C& rng, P p, T target) {
for (const auto& elem: rng) {
if (p(elem)){
target(elem);
}
}
}
constexpr bool is_even(int i) {
return i % 2 == 0;
}
template<std::size_t N>
constexpr std::size_t count_evens(const std::array<int, N>& arr)
{
std::size_t cnt = 0;
invoke_if(arr, is_even, [&cnt](auto&&){++cnt;});
return cnt;
}
template<std::size_t cnt, std::size_t N>
static constexpr auto get_evens(const std::array<int, N>& arr) {
std::array<int, cnt> result{};
int idx = 0;
invoke_if(arr, is_even, [&result, &idx](const auto& val){ result[idx++] = val;});
return result;
}
int main() {
// constexpr std::array<int, 5> arr{11, 22, 33, 44, 55};
for (const int i:get_evens<count_evens(arr)>(arr)) {
std::cout << i << " " << std::endl;
}
}
this works in g++, but in clang we get a problem because the begin on an array isn't properly constexpr with at least one library. Or maybe g++ violates the standard and clang does not.
template<auto t0, auto...ts>
struct ct_array:
std::array<decltype(t0) const, 1+sizeof...(ts)>,
std::integer_sequence<decltype(t0), t0, ts...>
{
ct_array():std::array<decltype(t0) const, 1+sizeof...(ts)>{{t0, ts...}} {};
};
template<class target, auto X>
struct push;
template<auto X>
struct push<void, X>{using type=ct_array<X>;};
template<auto...elems, auto X>
struct push<ct_array<elems...>, X>{using type=ct_array<elems...,X>;};
template<class target, auto X>
using push_t= typename push<target, X>::type;
template<class target>
struct pop;
template<auto x>
struct pop<ct_array<x>>{using type=void;};
template<auto x0, auto...xs>
struct pop<ct_array<x0, xs...>>{using type=ct_array<xs...>;};
template<class target>
using pop_t=typename pop<target>::type;
template<class lhs, class rhs, class F, class=void>
struct transcribe;
template<class lhs, class rhs, class F>
using transcribe_t = typename transcribe<lhs, rhs, F>::type;
template<auto l0, auto...lhs, class rhs, class F>
struct transcribe<ct_array<l0, lhs...>, rhs, F,
std::enable_if_t<F{}(l0) && sizeof...(lhs)>
>:
transcribe<pop_t<ct_array<l0, lhs...>>, push_t<rhs, l0>, F>
{};
template<auto l0, auto...lhs, class rhs, class F>
struct transcribe<ct_array<l0, lhs...>, rhs, F,
std::enable_if_t<!F{}(l0) && sizeof...(lhs)>
>:
transcribe<pop_t<ct_array<l0, lhs...>>, rhs, F>
{};
template<auto lhs, class rhs, class F>
struct transcribe<ct_array<lhs>, rhs, F, void>
{
using type=std::conditional_t< F{}(lhs), push_t<rhs, lhs>, rhs >;
};
template<class lhs, class F>
using filter_t = transcribe_t<lhs, void, F>;
// C++20
//auto is_even = [](auto i)->bool{ return !(i%2); };
struct is_even_t {
template<class T>
constexpr bool operator()(T i)const{ return !(i%2); }
};
constexpr is_even_t is_even{};
template<auto...is>
static constexpr auto get_evens(ct_array<is...>) {
return filter_t< ct_array<is...>, decltype(is_even) >{};
}
Live example.
Test code:
auto arr = ct_array<11, 22, 33, 44, 55>{};
for (const int i : get_evens(arr)) {
std::cout << i << " " << std::endl;
}

Transform constexpr expressions into template non-type parameter

In the following example I want to transform some constexpr values into template non-type parameters.
Here I have some constexpr values a1 and a2. I can use there (also constexpr) data-member d as non-type template parameters for the template B.
But if I try to take place this sort of transformation in some function transform this clearly doen't work anymore, since the parameter tuple isn't considered constexpr.
#include <cstdint>
#include <tuple>
template<auto N>
struct A {
inline static constexpr auto size = N;
const uint8_t d[N] {};
};
template<auto N, auto... II>
struct B {};
constexpr auto a1 = A<2>{1, 2};
constexpr auto a2 = A<3>{2, 3, 4};
constexpr auto t1 = std::tuple(a1, a2);
constexpr auto b1 = B<a1.size, a1.d[0], a1.d[1]>{};
constexpr auto b2 = B<a2.size, a2.d[0], a2.d[1], a2.d[2]>{};
template<typename T>
constexpr auto transform(const T& tuple) {
constexpr auto x1 = std::get<0>(tuple); // NOK
constexpr auto b1 = B<x1.size, x1.d[0], x1.d[1]>{}; // NOK
return std::tuple(b1);
}
constexpr auto t2 = transform(t1);
int main() {
}
The question is then: how can I make this sort of transformation. My idea is to write a meta-function with tuple t1 as parameter. But I can't get it right ...
In C++17 you can pass a constexpr lambda returning your argument:
constexpr auto t2 = transform([&] { return t1; });
Inside the function you can "call" the lambda and assign to a constexpr:
template<class Tuple_>
constexpr auto transform(Tuple_ tuple_) {
constexpr auto tuple = tuple_();
// ...
Some research (thanks #wimalopaan) shows that this idiom has been described
in an audience comment to a talk at C++Now 2017 by an anonymous commenter (referring to "some discussion on slack")
in a blog post by Michael Park
http://coliru.stacked-crooked.com/a/073103acf18f80b6
#include <cstdint>
#include <iostream>
#include <tuple>
// not sure how robust this macro is...
#define CONSTEXPR_ARG(arg) [&] { return arg; }
template<auto N>
struct A {
inline static constexpr auto size = N;
const uint8_t d[N] {};
};
template<auto N, auto... II>
struct B {};
constexpr auto a1 = A<2>{1, 2};
constexpr auto a2 = A<3>{2, 3, 4};
constexpr auto t1 = std::tuple(a1, a2);
constexpr auto b1 = B<a1.size, a1.d[0], a1.d[1]>{};
constexpr auto b2 = B<a2.size, a2.d[0], a2.d[1], a2.d[2]>{};
template<class Tuple_>
constexpr auto transform(Tuple_ tuple_) {
constexpr auto tuple = tuple_();
constexpr auto x1 = std::get<0>(tuple); // NOK
constexpr auto b1 = B<x1.size, x1.d[0], x1.d[1]>{}; // NOK
return std::tuple(b1);
}
constexpr auto t2 = transform(CONSTEXPR_ARG(t1));
template<class T>
void inspect(const T& t) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main() {
inspect(t2);
}
Output:
void inspect(const T&) [with T = std::tuple<B<2, 1, 2> >]

Cumulative Product of Template Parameter Pack

I'm trying to initialise a static and constant array with the cumulative product of a template parameter pack:
template <int ...D>
class Foo
{
static const std::array<const Size, sizeof...(D)> _array;
};
template <int...D> const std::array<const int, sizeof...(D)> Foo<D...>::_array =
{ cumulative_product<D...>() };
How do I write the function cumulative_product<>(), such that it transforms D... into the cumulative product of D...? E.g.
Foo<1,2,3,4>::_array;// = {1,1*2,1*2*3,1*2*3*4} = {1,2,6,24}.
Solution: Massive thank you to #bogdan for your excellent C++14 solution, and improvements to my C++11 solution.
#include <array>
#include <iostream>
#define CPP_VERSION 11
#if CPP_VERSION >= 14
// Credit: #bogdan at http://stackoverflow.com/q/37373602/6367128
template<int... Args> constexpr std::array<int, sizeof...(Args)> cumulative_product(int seed = 1) { return{ { seed *= Args ... } }; }
#elif CPP_VERSION == 11
// Acknowledgement: Huge thank you to #bogdan for making the code more portable, concise and readable!
namespace details
{
template<int N, int i, int j, int ...Args> struct c_product_gen // N counts down to zero
{
static constexpr std::array<int, sizeof...(Args)+1> get() { return c_product_gen<N - 1, i*j, Args..., i*j>::get(); }
};
template<int i, int j, int ...Args> struct c_product_gen<0, i, j, Args...> // The end point of the template recursion
{
static constexpr std::array<int, sizeof...(Args)+1> get() { return { { j, Args... } }; }
};
}
template<int... Args> constexpr std::array<int, sizeof...(Args)> cumulative_product() { return details::c_product_gen<sizeof...(Args), 1, Args...>::get(); }
#else // CPP_VERSION < 11
template<int... Args> constexpr std::array<int, sizeof...(Args)> cumulative_product()
{
static_assert(false, "C++ version 11 or greater is required.");
return std::array<int, sizeof...(Args)>();
}
#endif
int main()
{
constexpr auto a = cumulative_product<1,2,3,4,5>();
for(auto i : a) std::cout << i << ' '; // Output: 1 2 6 24 120
std::cout << '\n';
}
#include <array>
#include <utility>
#include <cstddef>
template <int... D, std::size_t... Is>
constexpr std::array<int, sizeof...(D)> cumulative_product(std::index_sequence<Is...>)
{
static_assert(sizeof...(D), "Missing initializers");
int a[]{ D... };
for (int i = 1; i < int(sizeof...(D)); ++i)
{
a[i] *= a[i-1];
}
return {{ a[Is]... }};
}
template <int... D>
constexpr auto cumulative_product()
{
return cumulative_product<D...>(std::make_index_sequence<sizeof...(D)>{});
}
template <int... D>
struct Foo
{
static constexpr std::array<int, sizeof...(D)> _array = cumulative_product<D...>();
};
DEMO