Вероятно, это можно сделать более красиво, но в комментариях приведена попытка в соответствии с вашими требованиями.
Требуется C ++ 17, работает на Clang, но выдает Внутренняя ошибка компилятора на G CC.
Однако требуется, чтобы вы сделали конструкционную функцию дружественной к SFINAE, иначе нет способа проверить, может ли она быть вызвана:
Так что используйте
return [](auto... args) -> decltype(U(args)...) { return U(args...); };
вместо
return [](auto... args) { return U(args...); };
Поведение этой функции с учетом аргументов tup
и index
выглядит следующим образом:
Возвращает лямбда, которая при вызове со списком аргументов возвращает std::variant
всех типов, которые могут возникнуть в результате вызовов вида std::get<i>(tup)(/*arguments*/)
. Какой из них действительно вызывается и сохраняется в возвращенном варианте, определяется во время выполнения с помощью аргумента index
. Если index
относится к элементу кортежа, который не может быть вызван как бы std::get<index>(tup)(/*arguments*/)
, то во время выполнения выдается исключение.
Промежуточная лямбда может быть сохранена и вызвана позже. Тем не менее, обратите внимание, что он сохраняет ссылку на аргумент tup
, поэтому вам нужно убедиться, что аргумент перестаёт быть лямбда-выражением, если вы не вызываете его и не сбрасываете его немедленно. ++ 20, вы можете упростить это,
, используя std::remove_cvref_t<Tup>
вместо std::remove_const_t<std::remove_reference_t<Tup>>
, изменив определение unwrap
на :
template<auto A>
using unwrap = typename decltype(A)::type;
и использование его как unwrap<...>
вместо unwrap<+...>
, что также позволяет удалить operator+
из wrap_t
.
Цель wrap
/ unwrap
:
wrap_t
- превратить тип в значение, которое я могу передавать в функции и возвращать из них, не создавая объект исходного типа (который может вызвать все виды проблем). На самом деле это просто пустая структура, настроенная на тип и псевдоним типа type
, который возвращает тип.
Я написал wrap
как глобальную встроенную переменную, так что я могу написать wrap<int>
вместо из wrap<int>{}
, так как я считаю дополнительные скобки раздражающими.
unwrap<...>
не на самом деле не требуется. typename decltype(...)::type
делает то же самое, он просто возвращает тип, который представляет экземпляр wrap.
Но опять же я хотел найти более простой способ его написания, но без C ++ 20 это на самом деле невозможно в хороший способ В C ++ 20 я могу просто передать объект wrap
непосредственно в качестве аргумента шаблона, но это не работает в C ++ 17.
Так что в C ++ 17 я «разлагаю» объект на указатель, который может быть аргументом шаблона не-типа, с перегруженным operator+
, имитирующим синтаксис обычного трюка с лямбда-указателем на функцию, используя унарный оператор +
(но я мог бы использовать любой другой унарный оператор).
Фактическое значение указателя не имеет значения, мне нужен только тип, но аргумент шаблона должен быть константным выражением, поэтому я позволю ему быть нулевым указателем. Последнее требование заключается в том, что я не использую встроенный оператор адреса &
вместо перегруженного +
.