0

I need to POD-initialize a struct of multiple arrays from a factory function. How do I forward its parameters to the brace-init list that is required to create the POD-struct (C++11)? I get this error:

<source>: In instantiation of 'constexpr generic_option<N, K> create_option(const int (&&)[N], const int (&&)[K]) [with long unsigned int N = 2; long unsigned int K = 2]':
<source>:209:32:   required from here
<source>:204:48: error: array must be initialized with a brace-enclosed initializer
  204 |     return generic_option<N, K>{to_wait, to_set};
      |                                                ^
<source>:204:48: error: array must be initialized with a brace-enclosed initializer
<source>:205:1: error: body of 'constexpr' function 'constexpr generic_option<N, K> create_option(const int (&&)[N], const int (&&)[K]) [with long unsigned int N = 2; long unsigned int K = 2]' not a return-statement
  205 | }
      | ^

My code:

template <size_t N, size_t K>
struct generic_option
{
    int to_wait_[N];
    int to_set_[K];
};

template <size_t N, size_t K>
constexpr generic_option<N, K> create_option(const int (&&to_wait)[N], const int (&&to_set)[K]) {
    return generic_option<N, K>{to_wait, to_set};
}

int main()
{
    create_option({1,4}, {2,3});
}

The reason for this post is that I can find information on how to initialize a struct of arrays with brace initializers using literals. But I can't seem to find a resource that states how to initialize them using compile time variables such as the ones passed to the factory function.

cigien
  • 57,834
  • 11
  • 73
  • 112
glades
  • 3,778
  • 1
  • 12
  • 34
  • In C++14 and up, this is relatively simple using `std::index_sequence` . I think I've seen `std::index_sequence` backported to C++11. – Igor Tandetnik Mar 26 '22 at 02:14

1 Answers1

0

I'm afraid you will need to find another way to do that.

As the compiler tells you, when initialising a class member that is raw C array, you must specify each element individually within a pair of {}, even if the array is a prvalue.

Minimum reproducible example is probably this:

struct some_struct
{
    int array[2];
};

int(&& array)[2] = {0, 1};
//some_struct s {array}; //Won't compile
some_struct s2 {{array[0], array[1]}}; //Compiles

I suggest you use std::array instead of the raw C-style arrays.

template <std::size_t N, std::size_t K>
struct generic_option
{
    std::array<int, N> to_wait_;
    std::array<int, K> to_set_;
};

template <size_t N, size_t K>
constexpr generic_option<N, K> create_option(std::array<int, N>&& w, std::array<int, N>&& s)
{
    return generic_option<N, K>{w, s};
}

Note that std::array is also POD (fulfills std::is_standard_layout_v and std::is_trivial_v), but because it has an additional member (its size), sizeof(generic_option) will also increase (but not by much). You will also have to specify the template arguments manually here because no guidelines are available in the context (but since you're in C++11, you would probably have to do that anyway).


In C++17, you have an elegant a way (at least I find it elegant) to do that using by passing std::tuple&& as argument instead and creating the arrays with std::apply. But that solution will still require using std::array. Note that you could implement something like std::apply yourself, see the answers here.

template <typename... w_t, typename... s_t>
constexpr generic_option<
                         std::tuple_size_v<std::tuple<int, w_t...>>,
                         std::tuple_size_v<std::tuple<int, s_t...>>
                        >
create_option(std::tuple<int, w_t...>&& to_wait, std::tuple<int, s_t...>&& to_set)
{
    constexpr std::size_t N = std::tuple_size_v<std::tuple<int, w_t...>>;
    constexpr std::size_t K = std::tuple_size_v<std::tuple<int, s_t...>>;

    constexpr auto create_wait = [](auto&& ... x){ return std::array<int, N>{std::forward<decltype(x)>(x) ... }; };
    constexpr auto create_set = [](auto&& ... x){ return std::array<int, K>{std::forward<decltype(x)>(x) ... }; };

    return generic_option<N, K>
    {
        std::apply(create_wait, std::forward<decltype(to_wait)>(to_wait)),
        std::apply(create_set, std::forward<decltype(to_set)>(to_set)),
    };
}

You might be able to use fold expressions instead (still in C++17), but I cannot figure that out.

Erel
  • 512
  • 1
  • 5
  • 14