Можно ли определить, сколько имен переменных следует указывать в квадратных скобках, используя синтаксис структурированных привязок, чтобы соответствовать количеству элементов данных простой правой части? struct
?
Я хочу сделать часть общей библиотеки, которая использует структурированные привязки для разложения произвольных классов на составляющие. На данный момент нет вариационной версии структурированных привязок (и, я думаю, не может быть предложено для текущего синтаксиса), но моя первая мысль — сделать набор перегрузок некоторой функции decompose()
, который выполняет разложение struct
параметр в набор его составляющих. decompose()
должен быть перегружен числом параметров (которое struct
) данные членов. В настоящее время constexpr if
синтаксис также может быть использован для отправки этого. Но как я могу подражать что-то похожее на sizeof...
оператор для вышеуказанных целей? Я не могу использовать auto [a, b, c]
синтаксис где-то в конструкциях SFINAE, потому что это декларация разложения и AFAIK любая декларация не может быть использована внутри 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
Может быть constexpr
Лямбда позволит нам использовать их в качестве аргументов шаблона. Как вы думаете?
Будет ли это возможно с приходом Концепции?
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 — это константа, вы можете увеличить ее, как хотите, за счет времени компиляции и памяти.
Если данные в вашей структуре не могут быть созданы из значения своего собственного типа, они не будут работать в C ++ 14, но я считаю, что гарантированное исключение происходит в C ++ 17 здесь (!)
Превращение в структурированные привязки требует больше, чем куча ручного кода. Но как только вы это сделаете, вы сможете задать такие вопросы, как «что это за третий тип struct
» и тому подобное.
Если struct
могут быть разложены на структурированные связки без tuple_size
В зависимости от того, сколько всего нужно сделать, его жизненность определяется.
Unfortunetally std::tuple_size
не дружит с SFINAE даже в C ++ 17. Но типы, которые используют 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
из decltype(get<Is>(x))
и верни это.
Если это не удается, используйте выше construct_airity
и используйте это, чтобы понять, как использовать структурированные привязки для типа. Я, вероятно, тогда отправлю это в std::tie
, для единообразия.
Теперь у нас есть tuple_it
который принимает что-то вроде структурированного связывания и преобразует его в набор ссылок или значений.
Теперь оба пути сошлись, и ваш общий код стал проще!
Также существует линейный подход для нахождения «совокупной арности» (хотя и при тех же достаточно строгих обстоятельствах, что и в принятом ответе):
#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 &&
), из-за ошибка.