Ширина структурированных креплений

Можно ли определить, сколько имен переменных следует указывать в квадратных скобках, используя синтаксис структурированных привязок, чтобы соответствовать количеству элементов данных простой правой части? 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 Лямбда позволит нам использовать их в качестве аргументов шаблона. Как вы думаете?

Будет ли это возможно с приходом Концепции?

5

Решение

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 который принимает что-то вроде структурированного связывания и преобразует его в набор ссылок или значений.
Теперь оба пути сошлись, и ваш общий код стал проще!

6

Другие решения

Также существует линейный подход для нахождения «совокупной арности» (хотя и при тех же достаточно строгих обстоятельствах, что и в принятом ответе):

#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 &&), из-за ошибка.

Живой пример.

1

По вопросам рекламы [email protected]