Возвращение агрегатных переменных (структура) и синтаксиса для вариабельного шаблона C++17 "Руководство по выводу конструкции"
Использование структуры шаблона, такой как many
ниже можно вернуть фиксированный набор возможных неподвижных объектов и получить их, используя структурированное связывание C++17 (auto [a,b,c] = f();
объявляет переменные ab и c и присваивает их значение из f, возвращая, например, структуру или кортеж).
template<typename T1,typename T2,typename T3>
struct many {
T1 a;
T2 b;
T3 c;
};
// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;
auto f(){ return many{string(),5.7, unmovable()}; };
int main(){
auto [x,y,z] = f();
}
Как объяснено в этих двух вопросах и ответах ( Поддерживают ли std::tuple и std::pair инициализацию агрегатов? И особенно принятый ответ ecatmur, а также множественные возвращаемые значения (структурированные привязки) с неподвижными типами и гарантированный RVO в C++17), std::tuple
не поддерживает агрегатную инициализацию. Это означает, что его нельзя использовать для хранения и возврата неподвижных типов. Но простая структура, как many
Можно сделать это, что приводит к вопросу:
Можно ли создать вариадическую версию many
который работает с любым количеством аргументов?
Обновление: в шаблонной версии many
будет разрешен следующий синтаксис руководства?
template<typename Args...>
many(Args...) -> many<Args...>;
2 ответа
В C++17 агрегатная инициализация сможет инициализировать публичные базовые классы. Таким образом, вы можете использовать наследование + расширение пакета для создания такого класса. Чтобы он работал со структурированными привязками, вам нужно будет предоставить интерфейс кортежей: specialize std::tuple_size
а также std::tuple_element
и предоставить get
функция для вашего класса:
//Headers used by "many" class implementation
#include <utility>
#include <tuple>
namespace rw {
namespace detail {
template <size_t index, typename T>
struct many_holder
{ T value; };
template <typename idx_seq, typename... Types>
struct many_impl;
template <size_t... Indices, typename... Types>
struct many_impl<std::index_sequence<Indices...>, Types...>: many_holder<Indices, Types>...
{};
}
template <typename... Types>
struct many: detail::many_impl<typename std::make_index_sequence<sizeof...(Types)>, Types...>
{};
template<size_t N, typename... Types>
auto get(const rw::many<Types...>& data) -> const std::tuple_element_t<N, rw::many<Types...>>&
{
const rw::detail::many_holder<N, std::tuple_element_t<N, rw::many<Types...>>>& holder = data;
return holder.value;
}
}
namespace std {
template <typename... Types>
struct tuple_size<rw::many<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)>
{};
template< std::size_t N, class... Types >
struct tuple_element<N, rw::many<Types...> >
{ using type = typename tuple_element<N, std::tuple<Types...>>::type; };
}
//Headers used for testing
#include <iostream>
#include <string>
int main()
{
rw::many<int, std::string, int> x = {42, "Hello", 11};
std::cout << std::tuple_size<decltype(x)>() << '\n' << rw::get<1>(x);
}
Демо (сейчас работает только в clang 3.9): http://melpon.org/wandbox/permlink/9NBqkcbOuURFvypt
Примечания:
- В демоверсии есть закомментированная реализация
nth_type
Вы можете использовать для дополненияtuple_element
прямо и не откладыватьtuple_element<tuple>
реализация. get<many>
должен быть либо функцией-членомmany
или помещаться в связанное пространство имен для структурированных привязок к работе. Вы не должны перегружатьstd::get
(В любом случае это будет UB).- Я оставил реализацию
get
для неконстантных ссылок и ссылок на r-значения в качестве упражнения для читателя. - Нет примера использования структурированных привязок и направляющих, потому что clang их не поддерживает (на самом деле я не знаю ни одного компилятора, который их поддерживает). Теоретически
template<typename... Types> many(Types...) -> many<Types...>;
должно сработать.
Об этом шла дискуссия по std-предложениям на днях.
У нас пока нет окончательной формулировки или, в этом отношении, компилятора (который я знаю), который поддерживает руководства по выводам, но, согласно Ричарду Смиту, должно работать следующее руководство по выводам:
template<class A, class B>
struct Agg
{
A a;
B b;
};
template<class A, class B>
Agg(A a, B b) -> Agg<A, B>;
Agg agg{1, 2.0}; // deduced to Agg<int, double>
Так что да, руководство по выводу переменных для агрегата также должно работать и работать с синтаксисом инициализации агрегата. Это не будет работать без руководства по выводам, так как без руководства по выводам компилятору нужен конструктор.