我想找到一种方法来写以下内容:
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
对象是否使用复制构造函数进行初始化?是否发生了一些奇怪的复制省略魔法?这与我收到的错误消息有关吗?
这是一个有效的实现,是
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;
}