Search code examples
c++c++11template-meta-programmingstdarray

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?


Solution

  • 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