I am trying to implement a constexpr function "add42" that would allow me to do:
constexpr array<int,5> arr = {1,2,3,4,5};
constexpr array<int,5> arr2 = add42(0,arr); //I want arr2=={43,2,3,4,5}
That is, add an integer at a given index to an array statically (with constexpr). Since my array "arr" is immutable, I have to actually create a new one from "arr" and my index. That is why I coded this function:
template<int DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> intergerArray, ARGS... unpackedIntegers) -> array<int,DIM> {
return
( sizeof...(ARGS)==DIM ) ?
array<int,DIM>( {{unpackedIntegers...}} ) :
( (sizeof...(ARGS)-1)==index ) ?
add42(index, intergerArray, unpackedIntegers..., intergerArray[sizeof...(ARGS)-1]+42 ) :
add42(index, intergerArray, unpackedIntegers..., intergerArray[sizeof...(ARGS)-1] ) ;
}
That is, all the integers of my array are recursively unpacked from the array, added 42 if at the right index, and appened at the end of the ARGS list. When this arg list contains all the integers from the array, we are done so we can repack into a new array.
However I get this error (gcc 4.7.2)
error: no matching function for call to 'add42(int, const std::array<int, 5u>&)'|
note: candidate is:|
template<int DIM, class ... ARGS> constexpr std::array<int, DIM> add42(int, std::array<int, DIM>, ARGS ...)|
note: template argument deduction/substitution failed:|
note: mismatched types 'int' and '#'integer_cst' not supported by dump_type#<type error>'|
Can you explain me what is the problem and how to correct it ?
This question seems similar to C++11: Compile Time Calculation of Array but is not (At least, I am unable to figure out how to use it deirectly): here, I want to create an new array from an already existent one, not from a known sequence of integers.
EDIT
Now I get infinite instantiation, even when no recursion called. Here is a simplified example :
template<size_t DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> array<int,DIM> {
return
( true ) ?
array<int,DIM>( {{unpackedIntegers...}} ) :
add42(index, integerArray, unpackedIntegers..., integerArray[(sizeof...(ARGS)-1)] ) ;
}
Why does my compiler try to compile the last function call ?
EDIT 2
Apparently, I have to provide 2 function in order not to confuse the compiler:
template<size_t DIM, class... ARGS> constexpr auto
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> typename enable_if<sizeof...(ARGS)==DIM ,array<int,DIM>>::type
{
return array<int,DIM>( {{unpackedIntegers...}} );
}
template<size_t DIM, class... ARGS> constexpr auto
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> typename enable_if<sizeof...(ARGS)!=DIM ,array<int,DIM>>::type
{
return
( sizeof...(ARGS) == index ) ?
add42(index, integerArray, unpackedIntegers..., integerArray[sizeof...(ARGS)]+42) :
add42(index, integerArray, unpackedIntegers..., integerArray[sizeof...(ARGS)]) ;
}
But it still does not work:
recursively required from [[name of the second function]]
Apparently, a variadic function cannot call "recursively" one of its overloads. Am I right ? What workaround is possible ?
You should use
template<size_t DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> intergerArray, ARGS... unpackedIntegers)
since array's second parameter has type size_t, not int.
Unfortunately, std::array::operator[] is not constexpr.
Compiler options: just plain -std=c++11
gcc <= 4.6: -std=c++0x and replace the using directive with a typedef
#include <cstddef>
//#include <array>
#include <type_traits>
#include <iostream>
template < typename T, std::size_t dim >
struct c_array
{
T arr[dim];
constexpr T operator[](std::size_t index)
{ return arr[index]; }
T const* begin() const
{ return arr; }
T const* end() const
{ return arr+dim; }
};
// I like the overloaded version better (instead of enable_if) :)
template < typename T, std::size_t dim, typename... TT >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index, std::true_type, TT... pp)
{
return {{pp...}};
}
template < typename T, std::size_t dim, typename... TT >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index, std::false_type, TT... pp)
{
using test = std::integral_constant<bool, (sizeof...(pp)+1 == dim)>;
return index == sizeof...(pp)
? add_to(s, in, index, test{}, pp..., in[sizeof...(pp)]+s)
: add_to(s, in, index, test{}, pp..., in[sizeof...(pp)] );
}
// unfortunately, I don't know how to avoid this additional overload :(
template < typename T, std::size_t dim>
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index)
{
return add_to(s, in, index, std::false_type{});
}
constexpr c_array<int,5> arr = {1,2,3,4,5};
constexpr c_array<int,5> arr2 = add_to(42, arr, 0); //I want arr2=={43,2,3,4,5}
int main()
{
for(auto const& e : arr2)
{
std::cout << e << ", ";
}
}
Alternative version, slightly awkward usage syntax:
// helper; construct a sequence of non-type template arguments
template < std::size_t... tt_i >
struct seq
{};
template < std::size_t t_n, std::size_t... tt_i >
struct gen_seq
: gen_seq < t_n-1, t_n-1, tt_i...>
{};
template < std::size_t... tt_i >
struct gen_seq < 0, tt_i... >
: seq < tt_i... >
{};
template < std::size_t index, typename T, std::size_t dim,
std::size_t... tt_bef, std::size_t... tt_aft >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, seq<tt_bef...>, seq<tt_aft...>)
{
return {{ in[tt_bef]..., in[index]+s, in[tt_aft]... }};
}
template < std::size_t index, typename T, std::size_t dim >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in)
{
return add_to<index>(s, in, gen_seq<index>{}, gen_seq<dim-index-1>{});
}
constexpr c_array<int,5> arr = {1,2,3,4,5};
constexpr c_array<int,5> arr2 = add_to<0>(42, arr);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With