是否可以使用结构化绑定语法确定在方括号中指定的变量名称数量,以匹配普通右侧struct
的数据成员数?
我想创建通用库的一部分,它使用结构化绑定将任意类分解为其组成部分。目前没有结构化绑定的可变版本(并且,我认为,不能用于当前提出的语法),但我首先想到的是对一些函数decompose()
进行一组重载,它执行{的分解{1}}参数到一组成分中。 struct
应该被参数的数量(即decompose()
)数据成员重载。目前struct
语法也可用于分发此语法。但是,为了上述目的,我如何模拟与constexpr if
运算符类似的内容?我不能在SFINAE结构中的某处使用sizeof...
语法,因为它是分解声明和AFAIK任何声明都不能在auto [a, b, c]
内使用,我也不能用它来做我的lambda函数体中的用途,因为lambda函数也不能在模板参数中使用。
当然我想拥有内置运算符(decltype
/ sizeof[] S
的语法为sizeof[](S)
),但以下内容也是可以接受的:
class S
也许template< typename type, typename = void >
struct sizeof_struct
{
};
template< typename type >
struct sizeof_struct< type, std::void_t< decltype([] { auto && [p1] = std::declval< type >(); void(p1); }) > >
: std::integral_constant< std::size_t, 1 >
{
};
template< typename type >
struct sizeof_struct< type, std::void_t< decltype([] { auto && [p1, p2] = std::declval< type >(); void(p1); void(p2); }) > >
: std::integral_constant< std::size_t, 2 >
{
};
... etc up to some reasonable arity
lambda将允许我们将它们用于模板的参数中。你觉得怎么样?
是否可以使用 Concepts ?
答案 0 :(得分:7)
struct two_elements {
int x;
double y;
};
struct five_elements {
std::string one;
std::unique_ptr<int> two;
int * three;
char four;
std::array<two_elements, 10> five;
};
struct anything {
template<class T> operator T()const;
};
namespace details {
template<class T, class Is, class=void>
struct can_construct_with_N:std::false_type {};
template<class T, std::size_t...Is>
struct can_construct_with_N<T, std::index_sequence<Is...>, std::void_t< decltype(T{(void(Is),anything{})...}) >>:
std::true_type
{};
}
template<class T, std::size_t N>
using can_construct_with_N=details::can_construct_with_N<T, std::make_index_sequence<N>>;
namespace details {
template<std::size_t Min, std::size_t Range, template<std::size_t N>class target>
struct maximize:
std::conditional_t<
maximize<Min, Range/2, target>{} == (Min+Range/2)-1,
maximize<Min+Range/2, (Range+1)/2, target>,
maximize<Min, Range/2, target>
>
{};
template<std::size_t Min, template<std::size_t N>class target>
struct maximize<Min, 1, target>:
std::conditional_t<
target<Min>{},
std::integral_constant<std::size_t,Min>,
std::integral_constant<std::size_t,Min-1>
>
{};
template<std::size_t Min, template<std::size_t N>class target>
struct maximize<Min, 0, target>:
std::integral_constant<std::size_t,Min-1>
{};
template<class T>
struct construct_searcher {
template<std::size_t N>
using result = ::can_construct_with_N<T, N>;
};
}
template<class T, std::size_t Cap=20>
using construct_airity = details::maximize< 0, Cap, details::construct_searcher<T>::template result >;
这是对T
从0到20的最长构造空间的二进制搜索.20是常量,您可以在编译时和内存开销时增加它。
如果你的struct中的数据不能从它自己类型的rvalue构造,它在C ++ 14中不起作用,但我相信在C ++ 17中会发生guanteed elision(!)
将其转换为结构化绑定需要一堆手动代码。但是一旦你有了,你应该能够提出诸如“这个struct
的第三种类型”等问题。
如果struct
可以在没有完成tuple_size
工作的情况下分解为结构化绑定,那么它的空气性决定了它需要多少变量。
即使在C ++ 17中,不幸的是std::tuple_size
也不是SFINAE友好的。但是,使用tuple_size
部分的类型也需要ADL启用std::get
。
使用failure_tag get<std::size_t>(Ts const&...)
using std::get
创建名称空间。使用它来检测他们是否已在类型(get<0>
)上覆盖!std::is_same< get_type<T,0>, failure_tag >{}
,如果是,请沿着tuple_element
路径确定空气质量。将生成的元素填入std::tuple
的{{1}}并返回。
如果失败,请使用上面的decltype(get<Is>(x))
,并使用它来弄清楚如何在类型上使用结构化绑定。然后我可能会将其发送到construct_airity
,以保持一致。
我们现在有std::tie
,它接受任何结构化绑定,并将其转换为引用或值的元组。
现在两个路径都已融合,您的通用代码更容易!
答案 1 :(得分:1)
此外,还有一种线性方法可以找到“聚合方式”(尽管也是在同样严格的宽度范围内,如同在接受的答案中):
#include <type_traits>
#include <utility>
#include <tuple>
struct filler { template< typename type > operator type && (); };
template< typename aggregate,
typename index_sequence = std::index_sequence<>,
typename = void >
struct aggregate_arity
: index_sequence
{
};
template< typename aggregate,
std::size_t ...indices >
struct aggregate_arity< aggregate,
std::index_sequence< indices... >,
std::void_t< decltype(aggregate{(indices, std::declval< filler >())..., std::declval< filler >()}) > >
: aggregate_arity< aggregate,
std::index_sequence< indices..., sizeof...(indices) > >
{
};
template< std::size_t index, typename type >
constexpr
decltype(auto)
get(type & value) noexcept
{
constexpr std::size_t arity = aggregate_arity< std::remove_cv_t< type > >::size();
if constexpr (arity == 1) {
auto & [p1] = value;
if constexpr (index == 0) {
return (p1);
} else {
return;
}
} else if constexpr (arity == 2) {
auto & [p1, p2] = value;
if constexpr (index == 0) {
return (p1);
} else if constexpr (index == 1) {
return (p2);
} else {
return;
}
} else if constexpr (arity == 3) {
auto & [p1, p2, p3] = value;
if constexpr (index == 0) {
return (p1);
} else if constexpr (index == 1) {
return (p2);
} else if constexpr (index == 2) {
return (p3);
} else {
return;
}
} else /* extend it by yourself for higher arities */ {
return;
}
}
// main.cpp
#include <cstdlib>
#include <cassert>
namespace
{
using S = struct { int i; char c; bool b; };
S s{1, '2', true};
decltype(auto) i = get< 0 >(s);
decltype(auto) c = get< 1 >(s);
decltype(auto) b = get< 2 >(s);
static_assert(std::is_same< decltype(i), int & >{});
static_assert(std::is_same< decltype(c), char & >{});
static_assert(std::is_same< decltype(b), bool & >{});
static_assert(&i == &s.i);
static_assert(&c == &s.c);
static_assert(&b == &s.b);
}
int
main()
{
assert(i == 1);
assert(c == '2');
assert(b == true);
return EXIT_SUCCESS;
}
目前get()
的参数不能有const
顶级类型限定符(即type
可以是&&
和&
,但不是const &
和const &&
),由于bug。