3

Is it possible to do this

int foo(){
  static int i=0;
  ret = i++;
  return ret;
}
const std::array<int,3> arr = {{foo(),foo(),foo()}};

in a (template?) function or a way that specifies "call foo for the initialization of every member"?i.e.

const std::array<int,3> arr = fill_with_foo<3,foo>();

For context,arr is a buffer from a queue from which N elements will be read (known at compile time). Currently I am using code generation to create the long form, and I have a function which simply allocates a normal array, fills it with a for loop and returns the array, but I wonder if it is possible to have the buffer array const.

//Edit: unlike in the linked "duplicate", I need

int foo();

to be nondeterministic at compile time, i.e. I think constexpr is out of the question (as I said, it needs to read from a queue which is filled at runtime). I am mainly interested in eliding useless copies

Igor K
  • 33
  • 6

2 Answers2

6

Since C++ 14 one might make use of std::index_sequnce (or implement it manually for older versions):

namespace detail
{

template<typename T, std::size_t N, typename F, std::size_t... I>
constexpr std::array<T, N> construct(F&& func, std::index_sequence<I...>)
{
    return { { (static_cast<void>(I), func())... } };
}

template<typename T, std::size_t N, typename F>
constexpr std::array<T, N> construct(F&& func)
{
    return construct<T, N>(std::forward<F>(func), std::make_index_sequence<N>());
}

}

Then you can apply it as follows:

const auto array = detail::construct<T, 3>(foo);

FULL CODE

Also note that constexpr enables constructing std::array even at compile time.

EXAMPLE

underscore_d
  • 6,309
  • 3
  • 38
  • 64
Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
  • Are you sure calling non constexpr function `func` won't interfere with constexpr-ness of callee function? Also there are implementations of `index_sequence` in c++11 which are worth mentioning since tag. – W.F. Jul 12 '17 at 08:03
  • 1
    @W.F. sure, calling non constexpr function makes this solution non constexpr too, that is why I've written *"even at compile time"*, but not *"always at compile time"* :) – Edgar Rokjān Jul 12 '17 at 08:08
  • [here](https://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence) you go :) – W.F. Jul 12 '17 at 08:10
  • @W.F. Yeah, I guess I know what I'll read this evening :) – Edgar Rokjān Jul 12 '17 at 08:32
  • if I understand this correctly, this needs the function to be fully known at compile time, i.e. it is not possible to use a runtime queue as data source? – Igor K Jul 12 '17 at 15:06
  • 1
    @IgorK The fundamental property of `constexpr` is that it means the code _can_ be used in a `constexpr` context; it does not mean that it _must_ be. Code marked `constexpr` will work in other contexts too. Edgar and W.F. already discussed this above. – underscore_d Jul 12 '17 at 15:24
  • ah, so if I read this correctly, it basically creates an "invisible variadic template" with the call to make_index_sequence, then uses that to call the function repeatedly N times *at runtime*? Then this sounds like what I am looking for. I am on C++11 for now, but marking this as the answer anyway since at some point hopefully C++14 and higher will be industry standard... – Igor K Jul 13 '17 at 09:40
  • @IgorK it depends on the function. If it is `constexpr`, then `array` is created at *compile time*, as in the last example. But if the function is not `constexpr`, then all related stuff is moved to runtime. – Edgar Rokjān Jul 13 '17 at 09:45
  • The return line in the first method is overly complicated. It should be sufficient to write `return { fun()... };` or even `return { fun(I)... };`. Also if I'm not mistaken, the function `fun` should not contain a static member variable. – Romeo Valentin Sep 25 '19 at 12:46
3

Consider making a function return an std::array. NRVO in most compilers will elide the copy.

#include <array>
#include <iostream>

template <typename T, std::size_t N, typename F>
std::array<T, N> fill_array(F const &fn)
{
    std::array<T, N> arr;

    for (auto i = std::begin(arr); i != std::end(arr); ++i) {
        *i = fn();
    }

    return arr;
}

int foo(){
  static int i=0;
  return i++;
}

int main() {
    auto const arr = fill_array<int, 3>(foo);
    for (auto const &i : arr) {
        std::cout << i << '\n';
    }
    return 0;
}

Demo

cdhowie
  • 158,093
  • 24
  • 286
  • 300