Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiplying each element of an std::array at compile time

I would like to convert an std::array to another std::array, multiplying each of its elements by a specific number.

What I have right now obviously doesn't work:

#include <array>
#include <iostream>
#include <utility>

template <class T, size_t... Is, size_t N>
constexpr std::array<T, N> multiply(std::array<T, N> const &src,
                                  std::index_sequence<Is...>) {
    return std::array<T, N>{{src[Is]...}}; // How can I multiply each of src's elements?
}

int main(int argc, char *argv[]) {
    constexpr std::array<int, 3> arr = {1, 2, 3};
    constexpr auto t = multiply(arr, std::make_index_sequence<3>{});
    for (auto &el : t) std::cout << el << std::endl;
    return 0;
}

My question is: how can I iterate over each element at compile time or how can I apply the same function (in my case: multiply by 2) at compile time?

like image 455
syntagma Avatar asked Dec 01 '15 22:12

syntagma


1 Answers

You can do it in the following way:

template<typename T>
constexpr T mult(T const &a, T const &b) { return a * b; }

template <class T, size_t... Is, size_t N>
constexpr std::array<T, N> multiply(std::array<T, N> const &src, 
                                    std::index_sequence<Is...>) {
  return std::array<T, N>{{mult(src[Is], src[Is])...}}; 
}

Live Demo

Or if you want to multiply by a number you can change to:

template<typename T>
constexpr T mult(T const &a, T const &b) { return a * b; }

template <class T, size_t... Is, size_t N>
constexpr std::array<T, N> multiply(std::array<T, N> const &src, 
                                    std::index_sequence<Is...>, T const &mul) {
  return std::array<T, N>{{mult(src[Is], mul)...}}; 
}

Live Demo

As Explained in cppreference:

A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the types from the pack, in order. Pack expansions can only happen in pack expansion contexts. These essentially are :

  • braced initialization
  • initializer lists
  • aggregate initializations
  • function calls
  • array initializations

Edit:

As T.C. pointed in the comments you can also do it as simple as:

template <class T, size_t... Is, size_t N>
constexpr std::array<T, N> multiply(std::array<T, N> const &src, std::index_sequence<Is...>, T const &mul) {
  return std::array<T, N>{{(src[Is] * mul)...}}; 
}

Live Demo

like image 115
101010 Avatar answered Oct 15 '22 21:10

101010