Splatting структура - PullRequest
       43

Splatting структура

5 голосов
/ 14 июня 2019

У меня есть несколько подобных структур с увеличивающимся числом членов, но последовательное именование членов:

struct one { int a; };
struct two { int a; int b; };
struct three { int a; int b; int c; };

У меня также есть шаблонная функция, которую я хочу, чтобы один из этих членов структуры, splatted:

template <typename T, typename ... ARGS> // T will be one, two, or three
void func(ARGS... args); // This should take 1, 2, or 3, int arguments respectively

Я хочу, чтобы это можно было назвать примерно так:

two foo;

func<two>(splatter(foo));

Где splatter каким-то образом разделит foo, чтобы преобразовать в func<two>(foo.a, foo.b).

Я, очевидно, могу просто развернуть эту строку без splatter, но код, в котором я называю func, сам по себе удачно создан. Я пытался использовать initializer_list, но я не могу понять, как создать его на основе только одного типа шаблона.

К сожалению, мой компилятор также не поддерживает constexpr if для вызова func или построения initializer_list. Есть ли у меня другие варианты?

1 Ответ

1 голос
/ 14 июня 2019

Насколько я знаю, то, что вы описываете, не может быть сделано с помощью c ++.Или, если может, то это очень сложное решение.Причина в том, что вам нужно каким-то образом хранить указатели на функции доступа к членам класса, а затем вызывать их правильно с вашим реальным объектом.

Однако вы получаете аналогичную функциональность с перегрузкой, которую гораздо проще реализовать.Например, вы можете определить call_func, который вы перегружаете для своих типов:

#include <array>

// types
struct one {
  int a;
};
struct two {
  int a;
  int b;
};
struct three {
  int a;
  int b;
  int c;
};
template <class T>
struct more_complex_type {
  T a;
  T b;
};
// template function
template <typename T, typename... ARGS>
auto func(ARGS... args) {
  return std::array<T, sizeof...(args)>{args...};
}
// indirection overload
template <class T>
struct call_func_impl {};
template <>
struct call_func_impl<one> {
  auto call(one val) { return func<int>(val.a); }
};
template <>
struct call_func_impl<two> {
  auto call(two val) { return func<int>(val.a, val.b); };
};
template <>
struct call_func_impl<three> {
  auto call(three val) { return func<int>(val.a, val.b, val.c); };
};
template <class T>
struct call_func_impl<more_complex_type<T>> {
  auto call(more_complex_type<T> val) { return func<T>(val.a, val.b); };
};
// syntacting sugar
template <class T>
auto call_func(T val) {
  return call_func_impl<T>{}.call(val);
}
// tests
auto test_func() { return func<int>(1, 2, 3, 4, 5); }
auto test_func_of_one() {
  auto val = one{};
  return call_func(val);
}
auto test_func_of_two() {
  auto val = two{};
  return call_func(val);
}
auto test_func_of_three() {
  auto val = three{};
  return call_func(val);
}
auto test_func_of_more_complex_type() {
  auto val = more_complex_type<double>{};
  return call_func(val);
}

В этом примере используется перегруженный шаблон структуры для переноса вызова функции.Это может быть необязательным для вашего случая, так как у вас нет шаблонизированных типов.Вы могли бы просто перегрузить call_func.Однако этот подход позволяет вам определить вызов для more_complex_type, который шаблонизируется, так как частичная перегрузка функций в настоящее время невозможна в c ++.

...