如何将集合的参数包展平为成员初始化的 std::array ?

问题描述 投票:0回答:1

我想找到一种方法来写以下内容:

group a{1, 2};
group b{a, 3}; // synonym to group b{1, 2, 3}

我已经进入了一个仅部分有效的环境,其中普通参数的基本情况有效:

#include <cstdint>
#include <array>
#include <iostream>

template <typename Member, size_t Dim>
struct group;

template <typename... Args>
static constexpr inline std::array<std::common_type_t<Args...>, sizeof...(Args)> make_array
(Args &&... args) {
    return { args... };
}

template <size_t VDim, size_t... Indices>
static constexpr inline auto make_array_i
(auto &&... args_begin, std::index_sequence<Indices...>, const group<auto, VDim> &v, auto &&... args_end) {
    return make_array(args_begin..., v[Indices]..., args_end...);
}

template <size_t VDim>
static constexpr inline auto make_array
(auto &&... args_begin, const group<auto, VDim> &v, auto &&... args_end) {
    return make_array_i(args_begin..., std::make_index_sequence<VDim>{}, v, args_end...);
}

template <typename Member, size_t Dim>
struct group {
    template <typename... Args>
    constexpr group(Args &&... args) : dat(make_array(args...)) {}

    constexpr const Member &operator[](const size_t d) const {
        return dat[d];
    }

private:
    std::array<Member, Dim> dat;
};

int main() {
    group<int, 2> a{1, 2};
    // group<int, 3> b{1, a};
}

但是,代码无法针对

group b
对象进行编译,并显示消息
array must be initialized with a brace-enclosed initializer
。我很困惑,因为它本质上在
group a
情况下做了同样的事情,但表现得非常好。

我也尝试过在第一个

make_array
重载(
{{ args... }}
)中对参数进行双括号括起来,但这似乎也不起作用,有同样的问题。

我无法离开

std::array
,我宁愿保留一切
constexpr
。这是取自一段较大的代码,因此有方法的
static
定义。

对于那些好奇的人来说,这是完整的错误消息:

min.cpp: In instantiation of ‘constexpr group<Member, Dim>::group(Args&& ...) [with Args = {int, group<int, 2>&}; Member = int; long unsigned int Dim = 3]’:
min.cpp:41:22:   required from here
min.cpp:29:58: error: array must be initialized with a brace-enclosed initializer
   29 |         constexpr group(Args &&... args) : dat(make_array(args...)) {}
      |                                                ~~~~~~~~~~^~~~~~~~~
min.cpp: In instantiation of ‘constexpr group<Member, Dim>::group(Args&& ...) [with Args = {int&}; Member = int; long unsigned int Dim = 2]’:
min.cpp:11:19:   required from ‘constexpr std::array<typename std::common_type<_Tp>::type, sizeof... (Args)> make_array(Args&& ...) [with Args = {int&, group<int, 2>&}; typename std::common_type<_Tp>::type = group<int, 2>]’
min.cpp:29:51:   required from ‘constexpr group<Member, Dim>::group(Args&& ...) [with Args = {int, group<int, 2>&}; Member = int; long unsigned int Dim = 3]’
min.cpp:41:22:   required from here
min.cpp:29:58: error: array must be initialized with a brace-enclosed initializer
min.cpp: In instantiation of ‘constexpr group<Member, Dim>::group(Args&& ...) [with Args = {group<int, 2>&}; Member = int; long unsigned int Dim = 2]’:
min.cpp:11:19:   required from ‘constexpr std::array<typename std::common_type<_Tp>::type, sizeof... (Args)> make_array(Args&& ...) [with Args = {int&, group<int, 2>&}; typename std::common_type<_Tp>::type = group<int, 2>]’
min.cpp:29:51:   required from ‘constexpr group<Member, Dim>::group(Args&& ...) [with Args = {int, group<int, 2>&}; Member = int; long unsigned int Dim = 3]’
min.cpp:41:22:   required from here
min.cpp:29:58: error: array must be initialized with a brace-enclosed initializer

顺便说一句,

group a
对象是否使用复制构造函数进行初始化?是否发生了一些奇怪的复制省略魔法?这与我收到的错误消息有关吗?

c++ variadic-templates stdarray
1个回答
0
投票

这是一个有效的实现,是

constexpr
并正确转发元素,允许使用仅移动类型。元素类型必须是默认可构造的,这不是绝对必要的,但修复起来非常痛苦。

我们努力在不使用 C++20 的情况下编写此代码,但您需要向后移植

std::span
。从
std::span
继承有点值得怀疑,但应该基本上没问题,我懒得重写它。

直播

#include<array>
#include<span>
#include<algorithm>

using std::size_t;

template<typename T, typename = void>
struct value_type
{
    using type = T;
};

template<typename T>
struct value_type<T, std::void_t<typename T::value_type>>
{
    using type = typename T::value_type;
};

template<typename T>
using value_type_t = typename value_type<T>::type;

template<typename, size_t>
struct group;

template<typename T>
struct size
{
    static constexpr size_t value = 1;
};

template<typename T, size_t N>
struct size<std::array<T, N>>
{
    static constexpr size_t value = N;
};

template<typename T, size_t N>
struct size<group<T, N>>
{
    static constexpr size_t value = N;
};

template<typename T>
inline constexpr auto size_v = size<T>::value;

template<typename T>
struct move_span : std::span<T>
{
    using base = std::span<T>;
    using base::span;

    constexpr auto begin() const noexcept
    {
        return std::make_move_iterator(base::begin());
    }

    constexpr auto end() const noexcept
    {
        return std::make_move_iterator(base::end());
    }
};

template<typename T>
constexpr std::span<const T> make_span(const T& t)
{
    return {&t, 1};
}

template<typename T, std::enable_if_t<!std::is_reference_v<T>, int> = 0>
constexpr move_span<T> make_span(T&& t)
{
    return {&t, 1};
}

template<typename T, size_t N>
constexpr std::span<const T> make_span(const std::array<T, N>& arr)
{
    return arr;
}

template<typename T, size_t N>
constexpr move_span<T> make_span(std::array<T, N>&& arr)
{
    return arr;
}

template<typename T, size_t N>
struct group
{
    using value_type = T;

    struct span_tag_t {};

    template<typename... Ts>
    constexpr group(Ts&&... ts)
    : group{span_tag_t{}, make_span(std::forward<Ts>(ts))...}
    {
    }

    template<typename... Spans>
    constexpr group(span_tag_t, Spans... spans)
    {
        auto p = dat.data();
        ((p = std::copy(spans.begin(), spans.end(), p)), ...);
    }

    std::array<T, N> dat;
};

template<typename... Ts>
group(Ts...)
-> group<std::common_type_t<value_type_t<Ts>...>, (size_v<Ts> + ...)>;

template<typename T, size_t N>
constexpr move_span<T> make_span(group<T, N>&& g)
{
    return g.dat;
}

template<typename T, size_t N>
constexpr std::span<const T> make_span(const group<T, N>& g)
{
    return g.dat;
}
© www.soinside.com 2019 - 2024. All rights reserved.