как заполнить вектор аргументами шаблонной функции и каждому аргументу присвоить новое значение чтобы были отсортированны
template<typename... Ts>
void asort(Ts&... ts)
{
vector<Ts> arr{ (ts...,) }; // ?? как заполнить вектор аргументами ts
std::sort(begin(arr), end(arr));
for (size_t i = 0; i < arr.size(); i++)
{
ts[i] = arr[i]; // ?? каждой переменной пакета ts присвоить значение вектора
}
}
int main()
{
int a = 0, b = 99, c = 23;
asort(a, b, c); // сортируем переменные
cout << a << '\t' << b << '\t' << c << endl;
}
Я делаю функцию сортировки переменных. Написал псевдокод. Помогите с распаковкой аргументов.
Реализовал так:
template<typename T, typename... Ts>
void mysort(T& t, Ts&... ts)
{
vector<T> vars;
vars.push_back(t);
(vars.push_back(ts), ...);
sort(vars.begin(), vars.end());
vector<T*> vars2;
vars2.push_back(&t);
(vars2.push_back(&ts), ...);
for (size_t i = 0; i < vars.size(); i++)
{
*vars2[i] = vars[i];
}
}
int main()
{
string a = "x", b = "e", c = "a";
cout << a << ' ' << b << ' ' << c << endl;
mysort(a, b, c);
cout << a << ' ' << b << ' ' << c << endl;
}
Ответы (3 шт):
Поместить объекты разных типов в вектор _ не получится. Если нужно поместить значения аргументов, то вам нужна функция с переменным количеством аргументов одного типа. Эти аргументы будут последовательно расположены в коробке функции, поэтому имея адрес первого аргумента, можно обращаться ко всем остальным, если будет какой то маркер конца. Например, в аргумент передавая количество аргументов или при вызове функции, передать нулевой указатель(если аргументы являются указателями). Аргументами могут быть указатели, но со ссылками не получится, потому что ссылки не занимают память в стеке, и не будет возможность обращаться ко всем переданным объектам. Поэтому лучше возвращать из функции контейнер, а потом использовать по усмотрению:
template<typename Ts>
std::vector<Ts> asort(int n, Ts ts,...)
{
Ts* p = &ts;
std::vector<Ts> arr; // ?? как заполнить вектор аргументами ts
while (n--) {
arr.push_back(*p);
++p;
}
std::sort(arr.begin(), arr.end());
return arr;
}
В качестве примера присвоим значение последнего аргумента:
//первый аргумент _ это количество аргументов
int a = asort(3, 4, 5, 6).back();
Можно также написать функцию без указания количества аргументов. Тогда нужно воспользоваться вариантом надежней _ услугами va_list, va_start
Поместить значения в вектор не проблема (если они одного типа, что тоже можно проверить в compile time)
std::vector arr{ {ts...} };
Проблема потом присвоить аргументам соответствующие значения вектора. Для этого надо из вектора сделать std::tuple и сделать zip тапла std::tie(ts...) и этого тапла с операцией = или использовать = прямо на таплах. Либо costexpr for с std::get<I>(tup_1) = std::get<I>(tup_2). Оба способа требуют много бойлерплейта. Гораздо проще изменить дизайн функции и не использовать входные аргументы, как выходные параметры, а просто возвращать отсортированный вектор. Также можно использовать std::array, раз количество элементов заранее известно.
#include <iostream>
#include <cassert>
#include <tuple>
using namespace std;
template <size_t I>
struct visit_impl
{
template <typename T, typename F>
static auto visit(T& tup, size_t idx, F fun)
{
if (idx == I - 1)
return fun(&std::get<I - 1>(tup));
else
{
visit_impl<I - 1>::visit(tup, idx, fun);
};
}
};
template <>
struct visit_impl<0>
{
template <typename T, typename F>
static void visit(T& tup, size_t idx, F fun) { assert(false); }
};
template <typename F, typename... Ts>
auto visit_at(std::tuple<Ts...>& tup, size_t idx, F fun)
{
return visit_impl<sizeof...(Ts)>::visit(tup, idx, fun);
}
template<typename pred = std::greater<>, typename... Ts>
void sort(Ts&... ts)
{
tuple<Ts...> tuple{ts...};
auto get_val = [](auto* val) { return val; };
for (int startIndex = 0; startIndex < sizeof...(Ts) - 1; ++startIndex)
{
int smallestIndex = startIndex;
for (int currentIndex = startIndex + 1; currentIndex < sizeof...(Ts) - 1; ++currentIndex)
{
if (pred{}(*visit_at(tuple, currentIndex, get_val), *visit_at(tuple, smallestIndex, get_val) ))
smallestIndex = currentIndex;
}
std::swap(*visit_at(tuple, startIndex, get_val), *visit_at(tuple, smallestIndex, get_val));
}
size_t pos = 0;
(void)initializer_list {
(ts = *visit_at(tuple, pos, get_val), ++pos)...
};
}
int main()
{
int a = -12, b = 66, c = -1;
sort(a, b, c);
cout << a << ' ' << b << ' '<< c;
}
На основе кортежей. Можно заменить на другую сортировку