Vincent
Vincent

Reputation: 60371

Cast an std::array to another data type at compile time?

Is there a way in C++11 to cast an array of one type to another data type at compile-time :

#include <iostream>
#include <array>
#include <type_traits>

int main()
{
   static constexpr std::array<double, 3> darray{{1.5, 2.5, 3.5}};
   static constexpr std::array<int, 3> iarray(darray); // not working
   // Is there a way to cast an array to another data type ? 
   return 0;
}

Upvotes: 7

Views: 4855

Answers (4)

Vincent
Vincent

Reputation: 60371

I've found a very simple solution with a single variadic function:

#include <iostream>
#include <array>
#include <type_traits>

template<typename Type, typename OtherType, std::size_t Size, typename... Types, class = typename std::enable_if<sizeof...(Types) != Size>::type>
constexpr std::array<Type, Size> convert(const std::array<OtherType, Size> source, const Types... data);

template<typename Type, typename OtherType, std::size_t Size, typename... Types, class = typename std::enable_if<sizeof...(Types) == Size>::type, class = void>
constexpr std::array<Type, Size> convert(const std::array<OtherType, Size> source, const Types... data);

template<typename Type, typename OtherType, std::size_t Size, typename... Types, class>
constexpr std::array<Type, Size> convert(const std::array<OtherType, Size> source, const Types... data)
{
    return convert<Type>(source, data..., static_cast<const Type>(source[sizeof...(data)]));
}

template<typename Type, typename OtherType, std::size_t Size, typename... Types, class, class>
constexpr std::array<Type, Size> convert(const std::array<OtherType, Size> source, const Types... data)
{
    return std::array<Type, Size>{{data...}};
}

int main()
{
   static constexpr std::array<double, 3> darray{{1., 2., 3.}};
   static constexpr std::array<int, 3> iarray = convert<int>(darray);
   std::cout<<(std::integral_constant<int, iarray[2]>())<<std::endl;
   return 0;
}

Upvotes: 3

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145269

Instead of an unmaintainable mess of cryptic template code that won't even currently compile with the most commonly used C++ compiler, and avoiding ungood redundancy in the number specs, simply use a macro:

#include <iostream>
#include <array>
#include <type_traits>

#define MY_VALUES( T ) {T(1.5), T(2.5), T(3.5)}

int main()
{
    static constexpr std::array<double, 3>   darray  = { MY_VALUES( double ) };
    static constexpr std::array<int, 3>      iarray  = { MY_VALUES( int ) };
    // Whatever...
}

This is the kind of stuff macros are good at.

Just make sure to minimize the possibility of name collision by using an all uppercase macro name, and maybe some custom prefix.


General advice: don't be too clever, keep it simple.

Keep in mind, someone has to maintain it later.

Upvotes: 2

ecatmur
ecatmur

Reputation: 157344

No, but you can do it by hand fairly easily using the indices trick, assuming the implementation provides constexpr std::get (or equivalently a constexpr overload of operator[]):

#include <iostream>
#include <array>
#include <type_traits>

// http://loungecpp.wikidot.com/tips-and-tricks%3aindices
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...>: indices<Is...> {};

template<typename T, typename U, size_t i, size_t... Is>
constexpr auto array_cast_helper(
   const std::array<U, i> &a, indices<Is...>) -> std::array<T, i> {
   return {{static_cast<T>(std::get<Is>(a))...}};
}

template<typename T, typename U, size_t i>
constexpr auto array_cast(
   const std::array<U, i> &a) -> std::array<T, i> {
   // tag dispatch to helper with array indices
   return array_cast_helper<T>(a, build_indices<i>());
}

int main() {
   static constexpr std::array<double, 3> darray{{1.5, 2.5, 3.5}};
   static constexpr std::array<int, 3> iarray = array_cast<int>(darray);
}

If your implementation doesn't provide constexpr get or operator[], you can't use array as there's no current standard way to access array elements constexpr; your best bet is to use your own implementation of array with the constexpr extensions.

The constexpr library additions are proposed for addition to the standard in n3470.

Upvotes: 9

Konrad Rudolph
Konrad Rudolph

Reputation: 545588

You cannot cast, but you can copy:

static constexpr std::array<double, 3> darray{{1.5, 2.5, 3.5}};
std::array<int, 3> iarray;

std::copy(begin(darray), end(darray), begin(iarray));

Unfortunately iarray cannot be constexpr any more in this case.

Upvotes: 1

Related Questions